#! /usr/bin/env ruby require 'spec_helper' require 'facter/util/collection' require 'facter/util/nothing_loader' describe Facter::Util::Collection do let(:external_loader) { Facter::Util::NothingLoader.new } let(:internal_loader) do load = Facter::Util::Loader.new load.stubs(:load).returns nil load.stubs(:load_all).returns nil load end let(:collection) { Facter::Util::Collection.new(internal_loader, external_loader) } it "should delegate its load_all method to its loader" do internal_loader.expects(:load_all) collection.load_all end describe "when adding facts" do it "should create a new fact if no fact with the same name already exists" do collection.add(:myname) collection.fact(:myname).name.should == :myname end it "should accept options" do collection.add(:myname, :timeout => 1) { } end it "passes resolution specific options to the fact" do fact = Facter::Util::Fact.new(:myname) Facter::Util::Fact.expects(:new).with(:myname, {:timeout => 'myval'}).returns fact fact.expects(:add).with({:timeout => 'myval'}) collection.add(:myname, :timeout => "myval") {} end describe "and a block is provided" do it "should use the block to add a resolution to the fact" do fact = mock 'fact' fact.stubs(:extract_ldapname_option!) Facter::Util::Fact.expects(:new).returns fact fact.expects(:add) collection.add(:myname) {} end it "should discard resolutions that throw an exception when added" do Facter.expects(:warn).with(regexp_matches(/Unable to add resolve .* kaboom!/)) expect { collection.add('yay') do raise "kaboom!" end }.to_not raise_error expect(collection.value('yay')).to be_nil end end end describe "when only defining facts" do it "creates a new fact if no such fact exists" do fact = Facter::Util::Fact.new(:newfact) Facter::Util::Fact.expects(:new).with(:newfact, {}).returns fact expect(collection.define_fact(:newfact)).to equal fact end it "returns an existing fact if the fact has already been defined" do fact = collection.define_fact(:newfact) expect(collection.define_fact(:newfact)).to equal fact end it "passes options to newly generated facts" do Facter.stubs(:warnonce) fact = collection.define_fact(:newfact, :ldapname => 'NewFact') expect(fact.ldapname).to eq 'NewFact' end it "logs a warning if the fact could not be defined" do Facter.expects(:warn).with("Unable to add fact newfact: kaboom!") collection.define_fact(:newfact) do raise "kaboom!" end end end describe "when retrieving facts" do before do @fact = collection.add("YayNess") end it "should return the fact instance specified by the name" do collection.fact("YayNess").should equal(@fact) end it "should be case-insensitive" do collection.fact("yayness").should equal(@fact) end it "should treat strings and symbols equivalently" do collection.fact(:yayness).should equal(@fact) end it "should use its loader to try to load the fact if no fact can be found" do collection.internal_loader.expects(:load).with(:testing) collection.fact("testing") end it "should return nil if it cannot find or load the fact" do collection.internal_loader.expects(:load).with(:testing) collection.fact("testing").should be_nil end end describe "when returning a fact's value" do before do @fact = collection.add("YayNess", :value => "result") end it "should return the result of calling :value on the fact" do collection.value("YayNess").should == "result" end it "should be case-insensitive" do collection.value("yayness").should == "result" end it "should treat strings and symbols equivalently" do collection.value(:yayness).should == "result" end end it "should return the fact's value when the array index method is used" do collection.add("myfact", :value => "foo") collection["myfact"].should == "foo" end it "should have a method for flushing all facts" do fact = collection.add("YayNess") fact.expects(:flush) collection.flush end it "should have a method that returns all fact names" do collection.add(:one) collection.add(:two) collection.list.sort { |a,b| a.to_s <=> b.to_s }.should == [:one, :two] end describe "when returning a hash of values" do it "should return a hash of fact names and values with the fact names as strings" do collection.add(:one, :value => "me") collection.to_hash.should == {"one" => "me"} end it "should not include facts that did not return a value" do collection.add(:two, :value => nil) collection.to_hash.should_not be_include(:two) end end describe "when iterating over facts" do before do collection.add(:one, :value => "ONE") collection.add(:two, :value => "TWO") end it "should yield each fact name and the fact value" do facts = {} collection.each do |fact, value| facts[fact] = value end facts.should == {"one" => "ONE", "two" => "TWO"} end it "should convert the fact name to a string" do facts = {} collection.each do |fact, value| fact.should be_instance_of(String) end end it "should only yield facts that have values" do collection.add(:nil_fact, :value => nil) facts = {} collection.each do |fact, value| facts[fact] = value end facts.should_not be_include("nil_fact") end end describe "when no facts are loaded" do it "should warn when no facts were loaded" do Facter.expects(:warnonce).with("No facts loaded from #{internal_loader.search_path.join(File::PATH_SEPARATOR)}").once collection.fact("one") end end describe "external facts" do let(:external_loader) { SingleFactLoader.new(:test_fact, "fact value") } let(:collection) { Facter::Util::Collection.new(internal_loader, external_loader) } it "loads when a specific fact is requested" do collection.fact(:test_fact).value.should == "fact value" end it "loads when facts are listed" do collection.list.should == [:test_fact] end it "loads when all facts are iterated over" do facts = [] collection.each { |fact_name, fact_value| facts << [fact_name, fact_value] } facts.should == [["test_fact", "fact value"]] end it "are loaded only once" do external_loader.expects(:load).with(collection) collection.load_all collection.load_all end it "are reloaded after flushing" do external_loader.expects(:load).with(collection).twice collection.load_all collection.flush collection.load_all end end class SingleFactLoader def initialize(name, value) @name = name @value = value end def load(collection) collection.add(@name, :value => @value) end end end