#!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../spec_helper' require 'yaml' require 'tempfile' require 'puppet/util/storage' describe Puppet::Util::Storage do before(:all) do Puppet[:statedir] = Dir.tmpdir() end after(:all) do Puppet.settings.clear end before(:each) do Puppet::Util::Storage.clear() end describe "when caching a symbol" do it "should return an empty hash" do Puppet::Util::Storage.cache(:yayness).should == {} Puppet::Util::Storage.cache(:more_yayness).should == {} end it "should add the symbol to its internal state" do Puppet::Util::Storage.cache(:yayness) Puppet::Util::Storage.state().should == {:yayness=>{}} end it "should not clobber existing state when caching additional objects" do Puppet::Util::Storage.cache(:yayness) Puppet::Util::Storage.state().should == {:yayness=>{}} Puppet::Util::Storage.cache(:bubblyness) Puppet::Util::Storage.state().should == {:yayness=>{},:bubblyness=>{}} end end describe "when caching a Puppet::Type" do before(:all) do @file_test = Puppet::Type.type(:file).new(:name => "/yayness", :check => %w{checksum type}) @exec_test = Puppet::Type.type(:exec).new(:name => "/bin/ls /yayness") end it "should return an empty hash" do Puppet::Util::Storage.cache(@file_test).should == {} Puppet::Util::Storage.cache(@exec_test).should == {} end it "should add the resource ref to its internal state" do Puppet::Util::Storage.state().should == {} Puppet::Util::Storage.cache(@file_test) Puppet::Util::Storage.state().should == {"File[/yayness]"=>{}} Puppet::Util::Storage.cache(@exec_test) Puppet::Util::Storage.state().should == {"File[/yayness]"=>{}, "Exec[/bin/ls /yayness]"=>{}} end end describe "when caching invalid objects" do before(:all) do @bogus_objects = [ {}, [], "foo", 42, nil, Tempfile.new('storage_test') ] end it "should raise an ArgumentError" do @bogus_objects.each do |object| proc { Puppet::Util::Storage.cache(object) }.should raise_error() end end it "should not add anything to its internal state" do @bogus_objects.each do |object| begin Puppet::Util::Storage.cache(object) rescue Puppet::Util::Storage.state().should == {} end end end end it "should clear its internal state when clear() is called" do Puppet::Util::Storage.cache(:yayness) Puppet::Util::Storage.state().should == {:yayness=>{}} Puppet::Util::Storage.clear() Puppet::Util::Storage.state().should == {} end describe "when loading from the state file" do before do Puppet.settings.stubs(:use).returns(true) end describe "when the state file/directory does not exist" do before(:each) do transient = Tempfile.new('storage_test') @path = transient.path() transient.close!() end it "should not fail to load()" do FileTest.exists?(@path).should be_false() Puppet[:statedir] = @path proc { Puppet::Util::Storage.load() }.should_not raise_error() Puppet[:statefile] = @path proc { Puppet::Util::Storage.load() }.should_not raise_error() end it "should not lose its internal state when load() is called" do FileTest.exists?(@path).should be_false() Puppet::Util::Storage.cache(:yayness) Puppet::Util::Storage.state().should == {:yayness=>{}} Puppet[:statefile] = @path proc { Puppet::Util::Storage.load() }.should_not raise_error() Puppet::Util::Storage.state().should == {:yayness=>{}} end end describe "when the state file/directory exists" do before(:each) do @state_file = Tempfile.new('storage_test') @saved_statefile = Puppet[:statefile] Puppet[:statefile] = @state_file.path() end it "should overwrite its internal state if load() is called" do # Should the state be overwritten even if Puppet[:statefile] is not valid YAML? Puppet::Util::Storage.cache(:yayness) Puppet::Util::Storage.state().should == {:yayness=>{}} proc { Puppet::Util::Storage.load() }.should_not raise_error() Puppet::Util::Storage.state().should == {} end it "should restore its internal state if the state file contains valid YAML" do test_yaml = {'File["/yayness"]'=>{"name"=>{:a=>:b,:c=>:d}}} YAML.expects(:load).returns(test_yaml) proc { Puppet::Util::Storage.load() }.should_not raise_error() Puppet::Util::Storage.state().should == test_yaml end it "should initialize with a clear internal state if the state file does not contain valid YAML" do @state_file.write(:booness) @state_file.flush() proc { Puppet::Util::Storage.load() }.should_not raise_error() Puppet::Util::Storage.state().should == {} end it "should raise an error if the state file does not contain valid YAML and cannot be renamed" do @state_file.write(:booness) @state_file.flush() YAML.expects(:load).raises(Puppet::Error) File.expects(:rename).raises(SystemCallError) proc { Puppet::Util::Storage.load() }.should raise_error() end it "should attempt to rename the state file if the file is corrupted" do # We fake corruption by causing YAML.load to raise an exception YAML.expects(:load).raises(Puppet::Error) File.expects(:rename).at_least_once proc { Puppet::Util::Storage.load() }.should_not raise_error() end it "should fail gracefully on load() if the state file is not a regular file" do @state_file.close!() Dir.mkdir(Puppet[:statefile]) File.expects(:rename).returns(0) proc { Puppet::Util::Storage.load() }.should_not raise_error() Dir.rmdir(Puppet[:statefile]) end it "should fail gracefully on load() if it cannot get a read lock on the state file" do Puppet::Util::FileLocking.expects(:readlock).yields(false) test_yaml = {'File["/yayness"]'=>{"name"=>{:a=>:b,:c=>:d}}} YAML.expects(:load).returns(test_yaml) proc { Puppet::Util::Storage.load() }.should_not raise_error() Puppet::Util::Storage.state().should == test_yaml end after(:each) do @state_file.close!() Puppet[:statefile] = @saved_statefile end end end describe "when storing to the state file" do before(:each) do @state_file = Tempfile.new('storage_test') @saved_statefile = Puppet[:statefile] Puppet[:statefile] = @state_file.path() end it "should create the state file if it does not exist" do @state_file.close!() FileTest.exists?(Puppet[:statefile]).should be_false() Puppet::Util::Storage.cache(:yayness) proc { Puppet::Util::Storage.store() }.should_not raise_error() FileTest.exists?(Puppet[:statefile]).should be_true() end it "should raise an exception if the state file is not a regular file" do @state_file.close!() Dir.mkdir(Puppet[:statefile]) Puppet::Util::Storage.cache(:yayness) proc { Puppet::Util::Storage.store() }.should raise_error() Dir.rmdir(Puppet[:statefile]) end it "should raise an exception if it cannot get a write lock on the state file" do Puppet::Util::FileLocking.expects(:writelock).yields(false) Puppet::Util::Storage.cache(:yayness) proc { Puppet::Util::Storage.store() }.should raise_error() end it "should load() the same information that it store()s" do Puppet::Util::Storage.cache(:yayness) Puppet::Util::Storage.state().should == {:yayness=>{}} proc { Puppet::Util::Storage.store() }.should_not raise_error() Puppet::Util::Storage.clear() Puppet::Util::Storage.state().should == {} proc { Puppet::Util::Storage.load() }.should_not raise_error() Puppet::Util::Storage.state().should == {:yayness=>{}} end after(:each) do @state_file.close!() Puppet[:statefile] = @saved_statefile end end end