Updated mcollective.init according to OSCI-658
[packages/precise/mcollective.git] / spec / unit / agents_spec.rb
diff --git a/spec/unit/agents_spec.rb b/spec/unit/agents_spec.rb
new file mode 100755 (executable)
index 0000000..ce01aad
--- /dev/null
@@ -0,0 +1,295 @@
+#!/usr/bin/env rspec
+
+require 'spec_helper'
+
+module MCollective
+  describe Agents do
+    before do
+      tmpfile = Tempfile.new("mc_agents_spec")
+      path = tmpfile.path
+      tmpfile.close!
+
+      @tmpdir = FileUtils.mkdir_p(path)
+      @tmpdir = @tmpdir[0] if @tmpdir.is_a?(Array) # ruby 1.9.2
+
+      @agentsdir = File.join([@tmpdir, "mcollective", "agent"])
+      FileUtils.mkdir_p(@agentsdir)
+
+      logger = mock
+      logger.stubs(:log)
+      logger.stubs(:start)
+      Log.configure(logger)
+    end
+
+    after do
+      FileUtils.rm_r(@tmpdir)
+    end
+
+    describe "#initialize" do
+      it "should fail if configuration has not been loaded" do
+        Config.instance.expects(:configured).returns(false)
+
+        expect {
+          Agents.new
+        }.to raise_error("Configuration has not been loaded, can't load agents")
+      end
+
+      it "should load agents" do
+        Config.instance.expects(:configured).returns(true)
+        Agents.any_instance.expects(:loadagents).once
+
+        Agents.new
+      end
+    end
+
+    describe "#clear!" do
+      it "should delete and unsubscribe all loaded agents" do
+        Config.instance.expects(:configured).returns(true).at_least_once
+        Config.instance.expects(:libdir).returns([@tmpdir])
+        PluginManager.expects(:delete).with("foo_agent").once
+        Util.expects(:make_subscriptions).with("foo", :broadcast).returns("foo_target")
+        Util.expects(:unsubscribe).with("foo_target")
+
+        a = Agents.new({"foo" => 1})
+      end
+    end
+
+    describe "#loadagents" do
+      before do
+        Config.instance.stubs(:configured).returns(true)
+        Config.instance.stubs(:libdir).returns([@tmpdir])
+        Agents.any_instance.stubs("clear!").returns(true)
+      end
+
+      it "should delete all existing agents" do
+        Agents.any_instance.expects("clear!").once
+        a = Agents.new
+      end
+
+      it "should attempt to load agents from all libdirs" do
+        Config.instance.expects(:libdir).returns(["/nonexisting", "/nonexisting"])
+        File.expects("directory?").with("/nonexisting/mcollective/agent").twice
+
+        a = Agents.new
+      end
+
+      it "should load found agents" do
+        Agents.any_instance.expects("loadagent").with("test").once
+
+        FileUtils.touch(File.join([@agentsdir, "test.rb"]))
+
+        a = Agents.new
+      end
+
+      it "should load each agent unless already loaded" do
+        Agents.any_instance.expects("loadagent").with("test").never
+
+        FileUtils.touch(File.join([@agentsdir, "test.rb"]))
+
+        PluginManager << {:type => "test_agent", :class => String.new}
+        a = Agents.new
+      end
+    end
+
+    describe "#loadagent" do
+      before do
+        FileUtils.touch(File.join([@agentsdir, "test.rb"]))
+        Config.instance.stubs(:configured).returns(true)
+        Config.instance.stubs(:libdir).returns([@tmpdir])
+        Agents.any_instance.stubs("clear!").returns(true)
+        PluginManager.stubs(:loadclass).returns(true)
+        PluginManager.stubs("[]").with("test_agent").returns(true)
+        Util.stubs(:make_subscriptions).with("test", :broadcast).returns([{:agent => "test", :type => :broadcast, :collective => "test"}])
+        Util.stubs(:subscribe).with([{:agent => "test", :type => :broadcast, :collective => "test"}]).returns(true)
+        Agents.stubs(:findagentfile).returns(File.join([@agentsdir, "test.rb"]))
+        Agents.any_instance.stubs("activate_agent?").returns(true)
+
+        @a = Agents.new
+      end
+
+      it "should return false if the agent file is missing" do
+        Agents.any_instance.expects(:findagentfile).returns(false).once
+        @a.loadagent("test").should == false
+      end
+
+      it "should delete the agent before loading again" do
+        PluginManager.expects(:delete).with("test_agent").twice
+        @a.loadagent("test")
+      end
+
+      it "should load the agent class from disk" do
+        PluginManager.expects(:loadclass).with("MCollective::Agent::Test")
+        @a.loadagent("test")
+      end
+
+      it "should check if the agent should be activated" do
+        Agents.any_instance.expects(:findagentfile).with("test").returns(File.join([@agentsdir, "test.rb"]))
+        Agents.any_instance.expects("activate_agent?").with("test").returns(true)
+        @a.loadagent("test").should == true
+      end
+
+      it "should set discovery and registration to be single instance plugins" do
+        PluginManager.expects("<<").with({:type => "registration_agent", :class => "MCollective::Agent::Registration", :single_instance => true}).once
+        PluginManager.expects("<<").with({:type => "discovery_agent", :class => "MCollective::Agent::Discovery", :single_instance => true}).once
+        Agents.any_instance.expects("activate_agent?").with("registration").returns(true)
+        Agents.any_instance.expects("activate_agent?").with("discovery").returns(true)
+
+        PluginManager.expects(:loadclass).with("MCollective::Agent::Registration").returns(true).once
+        PluginManager.expects(:loadclass).with("MCollective::Agent::Discovery").returns(true).once
+
+        FileUtils.touch(File.join([@agentsdir, "registration.rb"]))
+        FileUtils.touch(File.join([@agentsdir, "discovery.rb"]))
+
+        @a.loadagent("registration")
+        @a.loadagent("discovery")
+      end
+
+      it "should add general plugins as multiple instance plugins" do
+        PluginManager.expects("<<").with({:type => "test_agent", :class => "MCollective::Agent::Test", :single_instance => false}).once
+        @a.loadagent("test")
+      end
+
+      it "should add the agent to the plugin manager and subscribe" do
+        PluginManager.expects("<<").with({:type => "foo_agent", :class => "MCollective::Agent::Foo", :single_instance => false})
+        Util.stubs(:make_subscriptions).with("foo", :broadcast).returns([{:agent => "foo", :type => :broadcast, :collective => "test"}])
+        Util.expects("subscribe").with([{:type => :broadcast, :agent => 'foo', :collective => 'test'}]).returns(true)
+        Agents.any_instance.expects(:findagentfile).with("foo").returns(File.join([@agentsdir, "foo.rb"]))
+        FileUtils.touch(File.join([@agentsdir, "foo.rb"]))
+        Agents.any_instance.expects("activate_agent?").with("foo").returns(true)
+        PluginManager.stubs("[]").with("foo_agent").returns(true)
+
+        @a.loadagent("foo")
+      end
+
+      it "should check if an agent is loadable and remove it from the list if not" do
+        PluginManager.expects("<<").with({:type => "foo_agent", :class => "MCollective::Agent::Foo", :single_instance => false})
+        Agents.any_instance.expects(:findagentfile).with("foo").returns(File.join([@agentsdir, "foo.rb"]))
+        FileUtils.touch(File.join([@agentsdir, "foo.rb"]))
+        Agents.any_instance.expects("activate_agent?").with("foo").returns(true)
+        PluginManager.stubs("[]").with("foo_agent").raises("rspec")
+
+        Log.expects(:error).once.with("Loading agent foo failed: rspec")
+
+        @a.loadagent("foo").should == false
+        Agents.agentlist.include?("foo").should == false
+      end
+
+      it "should add the agent to the agent list" do
+        Agents.agentlist.should == ["test"]
+      end
+
+      it "should return true on success" do
+        @a.loadagent("test").should == true
+      end
+
+      it "should handle load exceptions" do
+        Agents.any_instance.expects(:findagentfile).with("foo").returns(File.join([@agentsdir, "foo.rb"]))
+        Log.expects(:error).with(regexp_matches(/Loading agent foo failed/))
+        @a.loadagent("foo").should == false
+      end
+
+      it "should delete plugins that failed to load" do
+        Agents.any_instance.expects(:findagentfile).with("foo").returns(File.join([@agentsdir, "foo.rb"]))
+        PluginManager.expects(:delete).with("foo_agent").twice
+
+        @a.loadagent("foo").should == false
+      end
+    end
+
+    describe "#class_for_agent" do
+      it "should return the correct class" do
+        Config.instance.stubs(:configured).returns(true)
+        Agents.any_instance.stubs(:loadagents).returns(true)
+        Agents.new.class_for_agent("foo").should == "MCollective::Agent::Foo"
+      end
+    end
+
+    describe "#activate_agent?" do
+      before do
+        Config.instance.stubs(:configured).returns(true)
+        Agents.any_instance.stubs(:loadagents).returns(true)
+        @a = Agents.new
+
+        module MCollective::Agent;end
+        class MCollective::Agent::Test; end
+      end
+
+      it "should check if the correct class has an activation method" do
+        Agent::Test.expects("respond_to?").with("activate?").once
+
+        @a.activate_agent?("test")
+      end
+
+      it "should call the activation method" do
+        Agent::Test.expects("activate?").returns(true).once
+        @a.activate_agent?("test")
+      end
+
+      it "should log a debug message and return true if the class has no activation method" do
+        Agent::Test.expects("respond_to?").with("activate?").returns(false).once
+        Log.expects(:debug).with("MCollective::Agent::Test does not have an activate? method, activating as default")
+
+        @a.activate_agent?("test").should == true
+      end
+
+      it "should handle exceptions in the activation as false" do
+        Agent::Test.expects("activate?").raises(RuntimeError)
+        @a.activate_agent?("test").should == false
+      end
+    end
+
+    describe "#findagentfile" do
+      before do
+        Config.instance.stubs(:configured).returns(true)
+        Config.instance.stubs(:libdir).returns([@tmpdir])
+        Agents.any_instance.stubs(:loadagents).returns(true)
+        @a = Agents.new
+      end
+
+      it "should support multiple libdirs" do
+        Config.instance.expects(:libdir).returns([@tmpdir, @tmpdir]).once
+        File.expects("exist?").returns(false).twice
+        @a.findagentfile("test")
+      end
+
+      it "should look for the correct filename in the libdir" do
+        File.expects("exist?").with(File.join([@tmpdir, "mcollective", "agent", "test.rb"])).returns(false).once
+        @a.findagentfile("test")
+      end
+
+      it "should return the full path if the agent is found" do
+        agentfile = File.join([@tmpdir, "mcollective", "agent", "test.rb"])
+        File.expects("exist?").with(agentfile).returns(true).once
+        @a.findagentfile("test").should == agentfile
+      end
+
+      it "should return false if no agent is found" do
+        @a.findagentfile("foo").should == false
+      end
+    end
+
+    describe "#include?" do
+      it "should correctly report the plugin state" do
+        Config.instance.stubs(:configured).returns(true)
+        Config.instance.stubs(:libdir).returns([@tmpdir])
+        Agents.any_instance.stubs(:loadagents).returns(true)
+        PluginManager.expects("include?").with("test_agent").returns(true)
+
+        @a = Agents.new
+
+        @a.include?("test").should == true
+      end
+    end
+
+    describe "#agentlist" do
+      it "should return the correct agent list" do
+        Config.instance.stubs(:configured).returns(true)
+        Config.instance.stubs(:libdir).returns([@tmpdir])
+        Agents.any_instance.stubs(:loadagents).returns(true)
+
+        @a = Agents.new("test" => true)
+        Agents.agentlist.should == ["test"]
+      end
+    end
+  end
+end