#!/usr/bin/env rspec require 'spec_helper' class MCollective::Connector::Stomp "connector_plugin", :class => MCollective::Connector::Stomp.new} end describe "#windows?" do it "should correctly detect windows on unix platforms" do RbConfig::CONFIG.expects("[]").returns("linux") Util.windows?.should == false end it "should correctly detect windows on windows platforms" do RbConfig::CONFIG.expects("[]").returns("win32") Util.windows?.should == true end end describe "#setup_windows_sleeper" do it "should set up a thread on the windows platform" do Thread.expects(:new) Util.expects("windows?").returns(true).once Util.setup_windows_sleeper end it "should not set up a thread on other platforms" do Thread.expects(:new).never Util.expects("windows?").returns(false).once Util.setup_windows_sleeper end end describe "#has_cf_class?" do before do logger = mock logger.stubs(:log) logger.stubs(:start) Log.configure(logger) config = mock config.stubs(:classesfile).returns("/some/file") Config.expects(:instance).returns(config) end it "should read the classes lines from the correct file" do File.expects(:readlines).with("/some/file") Util.has_cf_class?("test") end it "should support regular expression searches" do File.stubs(:readlines).returns(["test_class_test"]) String.any_instance.expects(:match).with("^/").returns(true) String.any_instance.expects(:match).with(Regexp.new("class")).returns(true) Util.has_cf_class?("/class/").should == true end it "should support exact string matches" do File.stubs(:readlines).returns(["test_class_test"]) String.any_instance.expects(:match).with("^/").returns(false) String.any_instance.expects(:match).with(Regexp.new("test_class_test")).never Util.has_cf_class?("test_class_test").should == true end it "should report a warning when the classes file cannot be parsed" do File.stubs(:readlines).returns(nil) Log.expects(:warn).with("Parsing classes file '/some/file' failed: NoMethodError: undefined method `each' for nil:NilClass") Util.has_cf_class?("test_class_test").should == false end end describe "#shellescape" do it "should return '' for empty strings" do Util.shellescape("").should == "''" end it "should quote newlines" do Util.shellescape("\n").should == "'\n'" end it "should escape unwanted characters" do Util.shellescape("foo;bar").should == 'foo\;bar' Util.shellescape('foo`bar').should == 'foo\`bar' Util.shellescape('foo$bar').should == 'foo\$bar' Util.shellescape('foo|bar').should == 'foo\|bar' Util.shellescape('foo&&bar').should == 'foo\&\&bar' Util.shellescape('foo||bar').should == 'foo\|\|bar' Util.shellescape('foo>bar').should == 'foo\>bar' Util.shellescape('foo:broadcast, :agent=>"test", :collective=>"collective1"}, {:type=>:broadcast, :agent=>"test", :collective=>"collective2"}] end it "should validate given collective" do Config.instance.stubs(:collectives).returns(["collective1", "collective2"]) expect { Util.make_subscriptions("test", :broadcast, "test") }.to raise_error("Unknown collective 'test' known collectives are 'collective1, collective2'") end it "should return a single subscription array given a collective" do Config.instance.stubs(:collectives).returns(["collective1", "collective2"]) Util.make_subscriptions("test", :broadcast, "collective1").should == [{:type=>:broadcast, :agent=>"test", :collective=>"collective1"}] end end describe "#subscribe" do it "should subscribe to multiple topics given an Array" do subs1 = {:agent => "test_agent", :type => "test_type", :collective => "test_collective"} subs2 = {:agent => "test_agent2", :type => "test_type2", :collective => "test_collective2"} MCollective::Connector::Stomp.any_instance.expects(:subscribe).with("test_agent", "test_type", "test_collective").once MCollective::Connector::Stomp.any_instance.expects(:subscribe).with("test_agent2", "test_type2", "test_collective2").once Util.subscribe([subs1, subs2]) end it "should subscribe to a single topic given a hash" do MCollective::Connector::Stomp.any_instance.expects(:subscribe).with("test_agent", "test_type", "test_collective").once Util.subscribe({:agent => "test_agent", :type => "test_type", :collective => "test_collective"}) end end describe "#unsubscribe" do it "should unsubscribe to multiple topics given an Array" do subs1 = {:agent => "test_agent", :type => "test_type", :collective => "test_collective"} subs2 = {:agent => "test_agent2", :type => "test_type2", :collective => "test_collective2"} MCollective::Connector::Stomp.any_instance.expects(:unsubscribe).with("test_agent", "test_type", "test_collective").once MCollective::Connector::Stomp.any_instance.expects(:unsubscribe).with("test_agent2", "test_type2", "test_collective2").once Util.unsubscribe([subs1, subs2]) end it "should subscribe to a single topic given a hash" do MCollective::Connector::Stomp.any_instance.expects(:unsubscribe).with("test_agent", "test_type", "test_collective").once Util.unsubscribe({:agent => "test_agent", :type => "test_type", :collective => "test_collective"}) end end describe "#empty_filter?" do it "should correctly compare empty filters" do Util.empty_filter?(Util.empty_filter).should == true end it "should treat an empty hash as an empty filter" do Util.empty_filter?({}).should == true end it "should detect non empty filters correctly" do filter = Util.empty_filter filter["cf_class"] << "meh" Util.empty_filter?(filter).should == false end end describe "#empty_filter" do it "should create correct empty filters" do Util.empty_filter.should == {"fact" => [], "cf_class" => [], "agent" => [], "identity" => [], "compound" => []} end end describe '#config_file_for_user' do before :each do File.stubs(:expand_path).raises File.stubs(:readable?).returns(false) Util.stubs(:windows?).returns(false) end it 'should default to /etc/puppetlabs/mcollective/client.cfg if ~/.mcollective can not be expanded' do Util.config_file_for_user.should == '/etc/puppetlabs/mcollective/client.cfg' end it 'should default to ~/.mcollective if it can be expanded' do File.expects(:expand_path).with('~/.mcollective').returns('/home/mco/.mcollective') Util.config_file_for_user.should == '/home/mco/.mcollective' end it 'should choose /etc/mcollective/client.cfg if it is readable' do File.expects(:readable?).with('/etc/mcollective/client.cfg').returns(true) Util.config_file_for_user.should == '/etc/mcollective/client.cfg' end end describe "#default_options" do it "should supply correct default options" do Config.instance.stubs(:default_discovery_options).returns([]) empty_filter = Util.empty_filter config_file = Util.config_file_for_user Util.default_options.should == {:verbose => false, :disctimeout => nil, :timeout => 5, :config => config_file, :filter => empty_filter, :collective => nil, :discovery_method => nil, :discovery_options => []} end end describe "#has_fact?" do it "should handle missing facts correctly" do MCollective::Facts.expects("[]").with("foo").returns(nil).once Util.has_fact?("foo", "1", "==").should == false end it "should handle regex in a backward compatible way" do MCollective::Facts.expects("[]").with("foo").returns("foo").times(6) Util.has_fact?("foo", "foo", "=~").should == true Util.has_fact?("foo", "/foo/", "=~").should == true Util.has_fact?("foo", "foo", "=~").should == true Util.has_fact?("foo", "bar", "=~").should == false Util.has_fact?("foo", "/bar/", "=~").should == false Util.has_fact?("foo", "bar", "=~").should == false end it "should evaluate equality" do MCollective::Facts.expects("[]").with("foo").returns("foo").twice Util.has_fact?("foo", "foo", "==").should == true Util.has_fact?("foo", "bar", "==").should == false end it "should handle numeric comparisons correctly" do MCollective::Facts.expects("[]").with("foo").returns("1").times(8) Util.has_fact?("foo", "2", ">=").should == false Util.has_fact?("foo", "1", ">=").should == true Util.has_fact?("foo", "2", "<=").should == true Util.has_fact?("foo", "1", "<=").should == true Util.has_fact?("foo", "1", "<").should == false Util.has_fact?("foo", "1", ">").should == false Util.has_fact?("foo", "1", "!=").should == false Util.has_fact?("foo", "2", "!=").should == true end it "should handle alphabetic comparisons correctly" do MCollective::Facts.expects("[]").with("foo").returns("b").times(8) Util.has_fact?("foo", "c", ">=").should == false Util.has_fact?("foo", "a", ">=").should == true Util.has_fact?("foo", "a", "<=").should == false Util.has_fact?("foo", "b", "<=").should == true Util.has_fact?("foo", "b", "<").should == false Util.has_fact?("foo", "b", ">").should == false Util.has_fact?("foo", "b", "!=").should == false Util.has_fact?("foo", "a", "!=").should == true end context 'structured facts (array)' do it "should handle regex in a backward compatible way" do MCollective::Facts.expects("[]").with("foo").returns(["foo", "baz"]).times(6) Util.has_fact?("foo", "foo", "=~").should == true Util.has_fact?("foo", "/foo/", "=~").should == true Util.has_fact?("foo", "foo", "=~").should == true Util.has_fact?("foo", "bar", "=~").should == false Util.has_fact?("foo", "/bar/", "=~").should == false Util.has_fact?("foo", "bar", "=~").should == false end it "should evaluate equality" do MCollective::Facts.expects("[]").with("foo").returns(["foo", "baz"]).twice Util.has_fact?("foo", "foo", "==").should == true Util.has_fact?("foo", "bar", "==").should == false end it "should handle numeric comparisons correctly" do MCollective::Facts.expects("[]").with("foo").returns(["1"]).times(8) Util.has_fact?("foo", "2", ">=").should == false Util.has_fact?("foo", "1", ">=").should == true Util.has_fact?("foo", "2", "<=").should == true Util.has_fact?("foo", "1", "<=").should == true Util.has_fact?("foo", "1", "<").should == false Util.has_fact?("foo", "1", ">").should == false Util.has_fact?("foo", "1", "!=").should == false Util.has_fact?("foo", "2", "!=").should == true end it "should handle alphabetic comparisons correctly" do MCollective::Facts.expects("[]").with("foo").returns(["b"]).times(8) Util.has_fact?("foo", "c", ">=").should == false Util.has_fact?("foo", "a", ">=").should == true Util.has_fact?("foo", "a", "<=").should == false Util.has_fact?("foo", "b", "<=").should == true Util.has_fact?("foo", "b", "<").should == false Util.has_fact?("foo", "b", ">").should == false Util.has_fact?("foo", "b", "!=").should == false Util.has_fact?("foo", "a", "!=").should == true end end context 'structured facts (hash)' do it "should handle regex in a backward compatible way" do MCollective::Facts.expects("[]").with("foo").returns({"foo" => 1, "baz" => 2}).times(6) Util.has_fact?("foo", "foo", "=~").should == true Util.has_fact?("foo", "/foo/", "=~").should == true Util.has_fact?("foo", "foo", "=~").should == true Util.has_fact?("foo", "bar", "=~").should == false Util.has_fact?("foo", "/bar/", "=~").should == false Util.has_fact?("foo", "bar", "=~").should == false end it "should evaluate equality" do MCollective::Facts.expects("[]").with("foo").returns({"foo" => 1, "baz" => 2}).twice Util.has_fact?("foo", "foo", "==").should == true Util.has_fact?("foo", "bar", "==").should == false end it "should handle numeric comparisons correctly" do MCollective::Facts.expects("[]").with("foo").returns({"1" => "one"}).times(8) Util.has_fact?("foo", "2", ">=").should == false Util.has_fact?("foo", "1", ">=").should == true Util.has_fact?("foo", "2", "<=").should == true Util.has_fact?("foo", "1", "<=").should == true Util.has_fact?("foo", "1", "<").should == false Util.has_fact?("foo", "1", ">").should == false Util.has_fact?("foo", "1", "!=").should == false Util.has_fact?("foo", "2", "!=").should == true end it "should handle alphabetic comparisons correctly" do MCollective::Facts.expects("[]").with("foo").returns({"b" => 2}).times(8) Util.has_fact?("foo", "c", ">=").should == false Util.has_fact?("foo", "a", ">=").should == true Util.has_fact?("foo", "a", "<=").should == false Util.has_fact?("foo", "b", "<=").should == true Util.has_fact?("foo", "b", "<").should == false Util.has_fact?("foo", "b", ">").should == false Util.has_fact?("foo", "b", "!=").should == false Util.has_fact?("foo", "a", "!=").should == true end end end describe 'test_fact_value' do it "should handle regex in a backward compatible way" do Util.send(:test_fact_value, "foo", "foo", "=~").should == true Util.send(:test_fact_value, "foo", "/foo/", "=~").should == true Util.send(:test_fact_value, "foo", "foo", "=~").should == true Util.send(:test_fact_value, "foo", "bar", "=~").should == false Util.send(:test_fact_value, "foo", "/bar/", "=~").should == false Util.send(:test_fact_value, "foo", "bar", "=~").should == false end it "should evaluate equality" do Util.send(:test_fact_value, "foo", "foo", "==").should == true Util.send(:test_fact_value, "foo", "bar", "==").should == false end it "should handle numeric comparisons correctly" do Util.send(:test_fact_value, "1", "2", ">=").should == false Util.send(:test_fact_value, "1", "1", ">=").should == true Util.send(:test_fact_value, "1", "2", "<=").should == true Util.send(:test_fact_value, "1", "1", "<=").should == true Util.send(:test_fact_value, "1", "1", "<").should == false Util.send(:test_fact_value, "1", "1", ">").should == false Util.send(:test_fact_value, "1", "1", "!=").should == false Util.send(:test_fact_value, "1", "2", "!=").should == true Util.send(:test_fact_value, "100", "2", ">").should == true Util.send(:test_fact_value, "100", "2", ">=").should == true Util.send(:test_fact_value, "100", "2", "<").should == false Util.send(:test_fact_value, "100", "2", "<=").should == false end it "should handle alphabetic comparisons correctly" do Util.send(:test_fact_value, "b", "c", ">=").should == false Util.send(:test_fact_value, "b", "a", ">=").should == true Util.send(:test_fact_value, "b", "a", "<=").should == false Util.send(:test_fact_value, "b", "b", "<=").should == true Util.send(:test_fact_value, "b", "b", "<").should == false Util.send(:test_fact_value, "b", "b", ">").should == false Util.send(:test_fact_value, "b", "b", "!=").should == false Util.send(:test_fact_value, "b", "a", "!=").should == true end end describe "#parse_fact_string" do it "should parse old style regex fact matches" do Util.parse_fact_string("foo=/bar/").should == {:fact => "foo", :value => "/bar/", :operator => "=~"} Util.parse_fact_string("foo = /bar/").should == {:fact => "foo", :value => "/bar/", :operator => "=~"} end it "should parse old style equality" do Util.parse_fact_string("foo=bar").should == {:fact => "foo", :value => "bar", :operator => "=="} Util.parse_fact_string("foo = bar").should == {:fact => "foo", :value => "bar", :operator => "=="} end it "should parse regex fact matches" do Util.parse_fact_string("foo=~bar").should == {:fact => "foo", :value => "bar", :operator => "=~"} Util.parse_fact_string("foo =~ bar").should == {:fact => "foo", :value => "bar", :operator => "=~"} end it "should treat => like >=" do Util.parse_fact_string("foo=>bar").should == {:fact => "foo", :value => "bar", :operator => ">="} Util.parse_fact_string("foo => bar").should == {:fact => "foo", :value => "bar", :operator => ">="} end it "should treat =< like <=" do Util.parse_fact_string("foo= "foo", :value => "bar", :operator => "<="} Util.parse_fact_string("foo =< bar").should == {:fact => "foo", :value => "bar", :operator => "<="} end it "should parse less than or equal" do Util.parse_fact_string("foo<=bar").should == {:fact => "foo", :value => "bar", :operator => "<="} Util.parse_fact_string("foo <= bar").should == {:fact => "foo", :value => "bar", :operator => "<="} end it "should parse greater than or equal" do Util.parse_fact_string("foo>=bar").should == {:fact => "foo", :value => "bar", :operator => ">="} Util.parse_fact_string("foo >= bar").should == {:fact => "foo", :value => "bar", :operator => ">="} end it "should parse less than" do Util.parse_fact_string("foo "foo", :value => "bar", :operator => "<"} Util.parse_fact_string("foo < bar").should == {:fact => "foo", :value => "bar", :operator => "<"} end it "should parse greater than" do Util.parse_fact_string("foo>bar").should == {:fact => "foo", :value => "bar", :operator => ">"} Util.parse_fact_string("foo > bar").should == {:fact => "foo", :value => "bar", :operator => ">"} end it "should parse greater than" do Util.parse_fact_string("foo>bar").should == {:fact => "foo", :value => "bar", :operator => ">"} Util.parse_fact_string("foo > bar").should == {:fact => "foo", :value => "bar", :operator => ">"} end it "should parse not equal" do Util.parse_fact_string("foo!=bar").should == {:fact => "foo", :value => "bar", :operator => "!="} Util.parse_fact_string("foo != bar").should == {:fact => "foo", :value => "bar", :operator => "!="} end it "should parse equal to" do Util.parse_fact_string("foo==bar").should == {:fact => "foo", :value => "bar", :operator => "=="} Util.parse_fact_string("foo == bar").should == {:fact => "foo", :value => "bar", :operator => "=="} end it "should fail for facts in the wrong format" do expect { Util.parse_fact_string("foo") }.to raise_error("Could not parse fact foo it does not appear to be in a valid format") end end describe "#colorize" do it "should not add color codes when color is disabled" do Config.instance.stubs(:color).returns(false) Util.colorize(:red, "hello world").should == "hello world" end it "should add color when color is enabled" do Config.instance.stubs(:color).returns(true) Util.colorize(:red, "hello world").should == "hello world" end end describe "#align_text" do it "should default to 80 if the terminal dimensions are unknown" do Util.stubs(:terminal_dimensions).returns([0,0]) rootdir = File.dirname(__FILE__) input = File.read("#{rootdir}/../../fixtures/util/4.in") output = File.read("#{rootdir}/../../fixtures/util/4.out") (Util.align_text(input, nil, 3) + "\n").should == output end it "should return the origional string if console lines are 0" do result = Util.align_text("test", 0) result.should == "test" end it "should return the origional string if preamble is greater than console lines" do result = Util.align_text("test", 5, 6) result.should == "test" end it "should return a string prefixed by the preamble" do result = Util.align_text("test") result.should == " test" end it "should correctly align strings" do rootdir = File.dirname(__FILE__) (1..2).each do |i| input = File.read("#{rootdir}/../../fixtures/util/#{i}.in") output = File.read("#{rootdir}/../../fixtures/util/#{i}.out") (Util.align_text(input, 158 , 5) + "\n").should == output end input = File.read("#{rootdir}/../../fixtures/util/3.in") output = File.read("#{rootdir}/../../fixtures/util/3.out") (Util.align_text(input, 30, 0) + "\n").should == output end end describe "#terminal_dimensions" do it "should return 0 if there is no tty" do stdout = mock() stdout.expects(:tty?).returns(false) result = Util.terminal_dimensions(stdout) result.should == [0,0] end it "should return the default dimensions for a windows terminal" do stdout = mock() stdout.expects(:tty?).returns(true) Util.expects(:windows?).returns(true) result = Util.terminal_dimensions(stdout) result.should == [80, 40] end it "should return 0 if an exception was raised" do stdout = mock() stdout.expects(:tty?).raises("error") result = Util.terminal_dimensions(stdout) result.should == [0, 0] end it "should return the correct dimensions if ENV columns and lines are set" do stdout = mock() stdout.expects(:tty?).returns(true) Util.expects(:windows?).returns(false) environment = mock() environment.expects(:[]).with("COLUMNS").returns(5).twice environment.expects(:[]).with("LINES").returns(5).twice result = Util.terminal_dimensions(stdout, environment) result.should == [5,5] end it "should return the correct dimensions if ENV term is set and tput is present" do stdout = mock() stdout.expects(:tty?).returns(true) Util.expects(:windows?).returns(false) environment = mock() environment.expects(:[]).with("COLUMNS").returns(false) environment.expects(:[]).with("TERM").returns(true) Util.expects(:command_in_path?).with("tput").returns(true) Util.stubs(:`).returns("5") result = Util.terminal_dimensions(stdout, environment) result.should == [5,5] end it "should return the correct dimensions if stty is present" do stdout = mock() stdout.expects(:tty?).returns(true) Util.expects(:windows?).returns(false) environment = mock() environment.expects(:[]).with("COLUMNS").returns(false) environment.expects(:[]).with("TERM").returns(false) Util.expects(:command_in_path?).with("stty").returns(true) Util.stubs(:`).returns("5 5") result = Util.terminal_dimensions(stdout, environment) result.should == [5,5] end end describe "#command_in_path?" do it "should return true if the command is found" do File.stubs(:exist?).returns(true) result = Util.command_in_path?("test") result.should == true end it "should return false if the command cannot be found" do File.stubs(:exist?).returns(false) result = Util.command_in_path?("test") result.should == false end end describe "#absolute_path?" do it "should work correctly validate the path" do Util.absolute_path?('.', '/', '\\').should == false Util.absolute_path?('foo/foo', '/', '\\').should == false Util.absolute_path?('foo\\bar', '/', '\\').should == false Util.absolute_path?('../foo/bar', '/', '\\').should == false Util.absolute_path?('\\foo/foo', '/', '\\').should == true Util.absolute_path?('\\', '/', '\\').should == true Util.absolute_path?('/foo', '/', '\\').should == true Util.absolute_path?('/foo/foo', '/', '\\').should == true Util.absolute_path?('.', '/', nil).should == false Util.absolute_path?('foo/foo', '/', nil).should == false Util.absolute_path?('foo\\bar', '/', nil).should == false Util.absolute_path?('../foo/bar', '/', nil).should == false Util.absolute_path?('\\foo/foo', '/', nil).should == false Util.absolute_path?('\\', '/', nil).should == false Util.absolute_path?('/foo', '/', nil).should == true Util.absolute_path?('/foo/foo', '/', nil).should == true end it "should correctly validate paths on Windows" do ['C:\foo', '\foo\bar', '\C\FOO\Bar', '/C/FOO/Bar'].each do |path| Util.absolute_path?(path, '/', '\\').should be_true end end end describe "#versioncmp" do it "should be able to sort a long set of various unordered versions" do ary = %w{ 1.1.6 2.3 1.1a 3.0 1.5 1 2.4 1.1-4 2.3.1 1.2 2.3.0 1.1-3 2.4b 2.4 2.40.2 2.3a.1 3.1 0002 1.1-5 1.1.a 1.06 1.2.10 1.2.8} newary = ary.sort {|a, b| Util.versioncmp(a,b) } newary.should == ["0002", "1", "1.06", "1.1-3", "1.1-4", "1.1-5", "1.1.6", "1.1.a", "1.1a", "1.2", "1.2.8", "1.2.10", "1.5", "2.3", "2.3.0", "2.3.1", "2.3a.1", "2.4", "2.4", "2.4b", "2.40.2", "3.0", "3.1"] end end describe "str_to_bool" do it "should transform true like strings into TrueClass" do ["1", "y", "yes", "Y", "YES", "t", "true", "T", "TRUE", true].each do |val| Util.str_to_bool(val).should be_true end end it "should transform false like strings into FalseClass" do ["0", "n", "no", "N", "NO", "f", "false", "F", "FALSE", false].each do |val| Util.str_to_bool(val).should be_false end end it "should raise an exception if the string does not match the boolean pattern" do ["yep", "nope", "yess", "noway", "rspec", "YES!", "NO?"].each do |val| expect { Util.str_to_bool(val) }.to raise_error("Cannot convert string value '#{val}' into a boolean.") end end end describe "#templatepath" do before do config = mock config.stubs(:configfile).returns("/rspec/server.cfg") Config.stubs(:instance).returns(config) end it "should look for a template in the config dir" do File.stubs(:exists?).with("/rspec/test-help.erb").returns(true) Util.templatepath("test-help.erb").should == "/rspec/test-help.erb" end it "should look for a template in the default dir" do File.stubs(:exists?).with("/rspec/test-help.erb").returns(false) File.stubs(:exists?).with("/etc/mcollective/test-help.erb").returns(true) Util.templatepath("test-help.erb").should == "/etc/mcollective/test-help.erb" end end describe "#field_size" do context "when elements are smaller than min_size" do it "should return min_size" do Util.field_size(['abc', 'def']).should == 40 end end context "when elements are bigger than min_size" do it "should return the size of the biggest element" do Util.field_size(['abc', 'def'], 2).should == 3 end end end describe "#field_number" do context "when field size is smaller than max_size" do it "should return field number" do Util.field_number(30).should == 3 end end context "when field size is smaller than max_size" do it "should return 1" do Util.field_number(100).should == 1 end end context "when specifying max_size" do it "should adapt to max_size" do Util.field_number(30, 70).should == 2 end end end end end