X-Git-Url: https://review.fuel-infra.org/gitweb?a=blobdiff_plain;f=spec%2Funit%2Frpc%2Fagent_spec.rb;fp=spec%2Funit%2Frpc%2Fagent_spec.rb;h=76630f24d5cb6bf253b333ea4fb6db482db0ffdc;hb=b87d2f4e68281062df1913440ca5753ae63314a9;hp=0000000000000000000000000000000000000000;hpb=ab0ea530b8ac956091f17b104ab2311336cfc250;p=packages%2Fprecise%2Fmcollective.git diff --git a/spec/unit/rpc/agent_spec.rb b/spec/unit/rpc/agent_spec.rb new file mode 100755 index 0000000..76630f2 --- /dev/null +++ b/spec/unit/rpc/agent_spec.rb @@ -0,0 +1,260 @@ +#!/usr/bin/env rspec + +require 'spec_helper' + +module MCollective + module RPC + describe Agent do + before do + ddl = stub + ddl.stubs(:meta).returns({}) + ddl.stubs(:action).returns([]) + ddl.stubs(:validate_rpc_request).returns(true) + DDL.stubs(:new).returns(ddl) + + @agent = Agent.new + @agent.reply = {} + @agent.request = {} + end + + describe "#handlemsg" do + before do + Reply.any_instance.stubs(:initialize_data) + + @agent.stubs(:respond_to?).with("rspec_action_action").returns(true) + @agent.stubs(:respond_to?).with("authorization_hook").returns(false) + @agent.stubs(:rspec_action_action).returns(nil) + + @msg = {:msgtime => 1356006671, + :senderid => "example.com", + :requestid => "55f8abe1442328321667877a08bdc586", + :body => {:agent => "rspec_agent", + :action => "rspec_action", + :data => {}}, + :caller => "cert=rspec"} + end + + it "should or validate the incoming request" do + exception = DDLValidationError.new(:RSPEC, "Failed to validate", :error) + Request.any_instance.expects(:validate!).raises(exception) + + reply = @agent.handlemsg(@msg, DDL.new) + + reply[:statuscode].should == 4 + reply[:statusmsg].should == "Failed to validate" + end + + it "should call the authorization hook if set" do + @agent.expects(:respond_to?).with("authorization_hook").returns(true) + @agent.expects(:authorization_hook).raises("authorization denied") + Log.stubs(:error) + + reply = @agent.handlemsg(@msg, DDL.new) + + reply[:statuscode].should == 5 + reply[:statusmsg].should == "authorization denied" + end + + it "should audit the request" do + @agent.expects(:audit_request) + + reply = @agent.handlemsg(@msg, DDL.new) + reply[:statuscode].should == 0 + end + + it "should call the before_processing_hook" do + @agent.expects(:before_processing_hook) + + reply = @agent.handlemsg(@msg, DDL.new) + reply[:statuscode].should == 0 + end + + it "should fail if the action does not exist" do + @agent.expects(:respond_to?).with("rspec_action_action").returns(false) + reply = @agent.handlemsg(@msg, DDL.new) + reply[:statuscode].should == 2 + end + + it "should call the action correctly" do + @agent.expects(:rspec_action_action) + reply = @agent.handlemsg(@msg, DDL.new) + reply[:statuscode].should == 0 + end + + it "should handle RPC Aborted errors" do + @agent.expects(:rspec_action_action).raises(RPCAborted, "rspec test") + reply = @agent.handlemsg(@msg, DDL.new) + reply[:statuscode].should == 1 + reply[:statusmsg].should == "rspec test" + end + + it "should handle Unknown Action errors" do + @agent.stubs(:respond_to?).with("rspec_action_action").returns(false) + reply = @agent.handlemsg(@msg, DDL.new) + reply[:statuscode].should == 2 + reply[:statusmsg].should == "Unknown action 'rspec_action' for agent 'rspec_agent'" + end + + it "should handle Missing Data errors" do + @agent.expects(:rspec_action_action).raises(MissingRPCData, "rspec test") + reply = @agent.handlemsg(@msg, DDL.new) + reply[:statuscode].should == 3 + reply[:statusmsg].should == "rspec test" + end + + it "should handle Invalid Data errors" do + @agent.expects(:rspec_action_action).raises(InvalidRPCData, "rspec test") + reply = @agent.handlemsg(@msg, DDL.new) + reply[:statuscode].should == 4 + reply[:statusmsg].should == "rspec test" + end + + it "should handle unknown errors" do + @agent.expects(:rspec_action_action).raises(UnknownRPCError, "rspec test") + Log.expects(:error).twice + + reply = @agent.handlemsg(@msg, DDL.new) + reply[:statuscode].should == 5 + reply[:statusmsg].should == "rspec test" + end + + it "should handle arbitrary exceptions" do + @agent.expects(:rspec_action_action).raises(Exception, "rspec test") + Log.expects(:error).twice + + reply = @agent.handlemsg(@msg, DDL.new) + reply[:statuscode].should == 5 + reply[:statusmsg].should == "rspec test" + end + + it "should call the after_processing_hook" do + @agent.expects(:after_processing_hook) + reply = @agent.handlemsg(@msg, DDL.new) + end + + it "should respond if required" do + Request.any_instance.expects(:should_respond?).returns(true) + Reply.any_instance.expects(:to_hash).returns({}) + @agent.handlemsg(@msg, DDL.new).should == {} + end + + it "should not respond when not required" do + Request.any_instance.expects(:should_respond?).returns(false) + Reply.any_instance.expects(:to_hash).never + @agent.handlemsg(@msg, DDL.new).should == nil + end + end + + describe "#meta" do + it "should be deprecated" do + Agent.expects(:log_code).with(:PLMC34, is_a(String), :warn, has_value(regexp_matches(/agent_spec.rb/))) + Agent.metadata("foo") + end + end + + describe "#load_ddl" do + it "should load the correct DDL" do + ddl = stub + ddl.stubs(:meta).returns({:timeout => 5}) + + DDL.expects(:new).with("agent", :agent).returns(ddl) + + Agent.new.timeout.should == 5 + end + + it "should fail if the DDL isn't loaded" do + DDL.expects(:new).raises("failed to load") + expect { Agent.new }.to raise_code(:PLMC24) + end + + it "should default to 10 second timeout" do + ddl = stub + ddl.stubs(:meta).returns({}) + + DDL.expects(:new).with("agent", :agent).returns(ddl) + + Agent.new.timeout.should == 10 + end + end + + describe "#run" do + before do + @status = mock + @status.stubs(:exitstatus).returns(0) + @shell = mock + @shell.stubs(:runcommand) + @shell.stubs(:status).returns(@status) + end + + it "should accept stderr and stdout and force them to be strings" do + Shell.expects(:new).with("rspec", {:stderr => "", :stdout => ""}).returns(@shell) + @agent.send(:run, "rspec", {:stderr => :err, :stdout => :out}) + @agent.reply[:err].should == "" + @agent.reply[:out].should == "" + end + + it "should accept existing variables for stdout and stderr and fail if they dont support <<" do + @agent.reply[:err] = "err" + @agent.reply[:out] = "out" + + Shell.expects(:new).with("rspec", {:stderr => "err", :stdout => "out"}).returns(@shell) + @agent.send(:run, "rspec", {:stderr => @agent.reply[:err], :stdout => @agent.reply[:out]}) + @agent.reply[:err].should == "err" + @agent.reply[:out].should == "out" + + @agent.reply.expects("fail!").with("stderr should support << while calling run(rspec)").raises("stderr fail") + expect { @agent.send(:run, "rspec", {:stderr => nil, :stdout => ""}) }.to raise_error("stderr fail") + + @agent.reply.expects("fail!").with("stdout should support << while calling run(rspec)").raises("stdout fail") + expect { @agent.send(:run, "rspec", {:stderr => "", :stdout => nil}) }.to raise_error("stdout fail") + end + + it "should set stdin, cwd and environment if supplied" do + Shell.expects(:new).with("rspec", {:stdin => "stdin", :cwd => "cwd", :environment => "env"}).returns(@shell) + @agent.send(:run, "rspec", {:stdin => "stdin", :cwd => "cwd", :environment => "env"}) + end + + it "should ignore unknown options" do + Shell.expects(:new).with("rspec", {}).returns(@shell) + @agent.send(:run, "rspec", {:rspec => "rspec"}) + end + + it "should chomp strings if configured to do so" do + Shell.expects(:new).with("rspec", {:stderr => 'err', :stdout => 'out'}).returns(@shell) + + @agent.reply[:err] = "err" + @agent.reply[:out] = "out" + + @agent.reply[:err].expects("chomp!") + @agent.reply[:out].expects("chomp!") + + @agent.send(:run, "rspec", {:chomp => true, :stdout => @agent.reply[:out], :stderr => @agent.reply[:err]}) + end + + it "should return the exitstatus" do + Shell.expects(:new).with("rspec", {}).returns(@shell) + @agent.send(:run, "rspec", {}).should == 0 + end + + it "should handle nil from the shell handler" do + @shell.expects(:status).returns(nil) + Shell.expects(:new).with("rspec", {}).returns(@shell) + @agent.send(:run, "rspec", {}).should == -1 + end + end + + describe "#validate" do + it "should detect missing data" do + @agent.request = {} + expect { @agent.send(:validate, :foo, String) }.to raise_error(MissingRPCData, "please supply a foo argument") + end + + it "should catch validation errors and turn them into use case specific ones" do + @agent.request = {:input_key => "should be a number"} + Validator.expects(:validate).raises(ValidatorError, "input_key should be a number") + expect { @agent.send(:validate, :input_key, :number) }.to raise_error("Input input_key did not pass validation: input_key should be a number") + end + end + end + end +end