X-Git-Url: https://review.fuel-infra.org/gitweb?a=blobdiff_plain;ds=sidebyside;f=spec%2Funit%2Fmessage_spec.rb;fp=spec%2Funit%2Fmessage_spec.rb;h=46ad7da6e2ee3ac2a244c90ca95e2ca05cdd0f35;hb=b87d2f4e68281062df1913440ca5753ae63314a9;hp=0000000000000000000000000000000000000000;hpb=ab0ea530b8ac956091f17b104ab2311336cfc250;p=packages%2Fprecise%2Fmcollective.git diff --git a/spec/unit/message_spec.rb b/spec/unit/message_spec.rb new file mode 100755 index 0000000..46ad7da --- /dev/null +++ b/spec/unit/message_spec.rb @@ -0,0 +1,423 @@ +#!/usr/bin/env rspec + +require 'spec_helper' + +module MCollective + describe Message do + before do + Config.instance.set_config_defaults("") + end + + describe "#initialize" do + it "should set defaults" do + m = Message.new("payload", "message") + m.payload.should == "payload" + m.message.should == "message" + m.request.should == nil + m.headers.should == {} + m.agent.should == nil + m.collective.should == nil + m.type.should == :message + m.filter.should == Util.empty_filter + m.requestid.should == nil + m.base64?.should == false + m.options.should == {} + m.discovered_hosts.should == nil + m.ttl.should == 60 + m.validated.should == false + m.msgtime.should == 0 + m.expected_msgid == nil + end + + it "should set all supplied options" do + Message.any_instance.expects(:base64_decode!) + + m = Message.new("payload", "message", :base64 => true, + :agent => "rspecagent", + :headers => {:rspec => "test"}, + :type => :rspec, + :filter => "filter", + :options => {:ttl => 30}, + :collective => "collective") + m.payload.should == "payload" + m.message.should == "message" + m.request.should == nil + m.headers.should == {:rspec => "test"} + m.agent.should == "rspecagent" + m.collective.should == "collective" + m.type.should == :rspec + m.filter.should == "filter" + m.base64?.should == true + m.options.should == {:ttl => 30} + m.ttl.should == 30 + end + + it "if given a request it should set options based on the request" do + request = mock + request.expects(:agent).returns("request") + request.expects(:collective).returns("collective") + + m = Message.new("payload", "message", :request => request) + m.agent.should == "request" + m.collective.should == "collective" + m.type.should == :reply + m.request.should == request + end + end + + describe "#reply_to=" do + it "should only set the reply-to header for requests" do + Config.instance.expects(:direct_addressing).returns(true) + m = Message.new("payload", "message", :type => :reply) + m.discovered_hosts = ["foo"] + expect { m.reply_to = "foo" }.to raise_error(/reply targets/) + + [:request, :direct_request].each do |t| + m.type = t + m.reply_to = "foo" + m.reply_to.should == "foo" + end + end + end + + describe "#expected_msgid=" do + it "should correctly set the property" do + m = Message.new("payload", "message", :type => :reply) + m.expected_msgid = "rspec test" + m.expected_msgid.should == "rspec test" + end + + it "should only be set for reply messages" do + m = Message.new("payload", "message", :type => :request) + + expect { + m.expected_msgid = "rspec test" + }.to raise_error("Can only store the expected msgid for reply messages") + end + end + + describe "#base64_decode!" do + it "should not decode if not encoded" do + SSL.expects(:base64_decode).never + m = Message.new("payload", "message") + end + + it "should decode encoded messages" do + SSL.expects(:base64_decode) + m = Message.new("payload", "message", :base64 => true) + end + + it "should set base64 to false after decoding" do + SSL.expects(:base64_decode).with("payload") + m = Message.new("payload", "message", :base64 => true) + m.base64?.should == false + end + end + + describe "#base64_encode" do + it "should not encode already encoded messages" do + SSL.expects(:base64_encode).never + Message.any_instance.stubs(:base64_decode!) + m = Message.new("payload", "message", :base64 => true) + m.base64_encode! + end + + it "should encode plain messages" do + SSL.expects(:base64_encode).with("payload") + m = Message.new("payload", "message") + m.base64_encode! + end + + it "should set base64 to false after encoding" do + SSL.expects(:base64_encode) + m = Message.new("payload", "message") + m.base64_encode! + m.base64?.should == true + end + end + + describe "#base64?" do + it "should correctly report base64 state" do + m = Message.new("payload", "message") + m.base64?.should == m.instance_variable_get("@base64") + end + end + + describe "#type=" do + it "should only allow types to be set when discovered hosts were given" do + m = Message.new("payload", "message") + Config.instance.stubs(:direct_addressing).returns(true) + + expect { + m.type = :direct_request + }.to raise_error("Can only set type to :direct_request if discovered_hosts have been set") + end + + it "should not allow direct_request to be set if direct addressing isnt enabled" do + m = Message.new("payload", "message") + Config.instance.stubs(:direct_addressing).returns(false) + + expect { + m.type = :direct_request + }.to raise_error("Direct requests is not enabled using the direct_addressing config option") + end + + it "should only accept valid types" do + m = Message.new("payload", "message") + Config.instance.stubs(:direct_addressing).returns(true) + + expect { + m.type = :foo + }.to raise_error("Unknown message type foo") + end + + it "should clear the filter in direct_request mode and add just an agent filter" do + m = Message.new("payload", "message") + m.discovered_hosts = ["rspec"] + Config.instance.stubs(:direct_addressing).returns(true) + + m.filter = Util.empty_filter.merge({"cf_class" => ["test"]}) + m.agent = "rspec" + m.type = :direct_request + m.filter.should == Util.empty_filter.merge({"agent" => ["rspec"]}) + end + + it "should set the type" do + m = Message.new("payload", "message") + m.type = :request + m.type.should == :request + end + end + + describe "#encode!" do + it "should encode replies using the security plugin #encodereply" do + request = mock + request.stubs(:agent).returns("rspec_agent") + request.stubs(:collective).returns("collective") + request.stubs(:payload).returns({:requestid => "123", :callerid => "id=callerid"}) + + security = mock + security.expects(:encodereply).with('rspec_agent', 'payload', '123', 'id=callerid') + security.expects(:valid_callerid?).with("id=callerid").returns(true) + + PluginManager.expects("[]").with("security_plugin").returns(security).twice + + m = Message.new("payload", "message", :request => request, :type => :reply) + + m.encode! + end + + it "should encode requests using the security plugin #encoderequest" do + security = mock + security.expects(:encoderequest).with("identity", 'payload', '123', Util.empty_filter, 'rspec_agent', 'mcollective', 60).twice + PluginManager.expects("[]").with("security_plugin").returns(security).twice + + Config.instance.expects(:identity).returns("identity").twice + + Message.any_instance.expects(:requestid).returns("123").twice + + m = Message.new("payload", "message", :type => :request, :agent => "rspec_agent", :collective => "mcollective") + m.encode! + + m = Message.new("payload", "message", :type => :direct_request, :agent => "rspec_agent", :collective => "mcollective") + m.encode! + end + + it "should retain the requestid if it was specifically set" do + security = mock + security.expects(:encoderequest).with("identity", 'payload', '123', Util.empty_filter, 'rspec_agent', 'mcollective', 60) + PluginManager.expects("[]").with("security_plugin").returns(security) + + Config.instance.expects(:identity).returns("identity") + + m = Message.new("payload", "message", :type => :request, :agent => "rspec_agent", :collective => "mcollective") + m.expects(:create_reqid).never + m.requestid = "123" + m.encode! + m.requestid.should == "123" + end + + it "should not allow bad callerids when replying" do + request = mock + request.stubs(:agent).returns("rspec_agent") + request.stubs(:collective).returns("collective") + request.stubs(:payload).returns({:requestid => "123", :callerid => "caller/id"}) + + security = mock + security.expects(:valid_callerid?).with("caller/id").returns(false) + PluginManager.expects("[]").with("security_plugin").returns(security) + + m = Message.new("payload", "message", :request => request, :type => :reply) + + expect { + m.encode! + }.to raise_error('callerid in original request is not valid, surpressing reply to potentially forged request') + end + end + + describe "#decode!" do + it "should check for valid types" do + expect { + m = Message.new("payload", "message", :type => :foo) + m.decode! + }.to raise_error("Cannot decode message type foo") + end + + it "should set state based on decoded message" do + msg = mock + msg.stubs(:include?).returns(true) + msg.stubs("[]").with(:collective).returns("collective") + msg.stubs("[]").with(:agent).returns("rspecagent") + msg.stubs("[]").with(:filter).returns("filter") + msg.stubs("[]").with(:requestid).returns("1234") + msg.stubs("[]").with(:ttl).returns(30) + msg.stubs("[]").with(:msgtime).returns(1314628987) + + security = mock + security.expects(:decodemsg).returns(msg) + PluginManager.expects("[]").with("security_plugin").returns(security) + + m = Message.new(msg, "message", :type => :reply) + m.decode! + + m.collective.should == "collective" + m.agent.should == "rspecagent" + m.filter.should == "filter" + m.requestid.should == "1234" + m.ttl.should == 30 + end + + it "should not allow bad callerids from the security plugin on requests" do + security = mock + security.expects(:decodemsg).returns({:callerid => "foo/bar"}) + security.expects(:valid_callerid?).with("foo/bar").returns(false) + + PluginManager.expects("[]").with("security_plugin").returns(security).twice + + m = Message.new("payload", "message", :type => :request) + + expect { + m.decode! + }.to raise_error('callerid in request is not valid, surpressing reply to potentially forged request') + end + end + + describe "#validate" do + it "should only validate requests" do + m = Message.new("msg", "message", :type => :reply) + expect { + m.validate + }.to raise_error("Can only validate request messages") + end + + it "should raise an exception for incorrect messages" do + sec = mock + sec.expects("validate_filter?").returns(false) + PluginManager.expects("[]").with("security_plugin").returns(sec) + + payload = mock + payload.expects("[]").with(:filter).returns({}) + + m = Message.new(payload, "message", :type => :request) + m.instance_variable_set("@msgtime", Time.now.to_i) + + expect { + m.validate + }.to raise_error(NotTargettedAtUs) + end + + it "should pass for good messages" do + sec = mock + sec.expects(:validate_filter?).returns(true) + PluginManager.expects("[]").returns(sec) + + payload = mock + payload.expects("[]").with(:filter).returns({}) + m = Message.new(payload, "message", :type => :request) + m.instance_variable_set("@msgtime", Time.now.to_i) + m.validate + end + + it "should set the @validated property" do + sec = mock + sec.expects(:validate_filter?).returns(true) + PluginManager.expects("[]").returns(sec) + + payload = mock + payload.expects("[]").with(:filter).returns({}) + m = Message.new(payload, "message", :type => :request) + m.instance_variable_set("@msgtime", Time.now.to_i) + + m.validated.should == false + m.validate + m.validated.should == true + end + + it "should not validate for messages older than TTL" do + stats = mock + stats.expects(:ttlexpired).once + + MCollective::PluginManager << {:type => "global_stats", :class => stats} + + m = Message.new({:callerid => "caller", :senderid => "sender"}, "message", :type => :request) + m.instance_variable_set("@msgtime", (Time.now.to_i - 120)) + + expect { + m.validate + }.to raise_error(MsgTTLExpired) + end + end + + describe "#publish" do + it "should publish itself to the connector" do + m = Message.new("msg", "message", :type => :request) + + connector = mock + connector.expects(:publish).with(m) + PluginManager.expects("[]").returns(connector) + + m.publish + end + + it "should support direct addressing" do + m = Message.new("msg", "message", :type => :request) + m.discovered_hosts = ["one", "two", "three"] + + Config.instance.stubs(:direct_addressing).returns(true) + Config.instance.stubs(:direct_addressing_threshold).returns(10) + + connector = mock + connector.expects(:publish).with(m) + PluginManager.expects("[]").returns(connector) + + m.publish + m.type.should == :direct_request + end + + it "should only direct publish below the configured threshold" do + m = Message.new("msg", "message", :type => :request) + m.discovered_hosts = ["one", "two", "three"] + + Config.instance.expects(:direct_addressing).returns(true) + Config.instance.expects(:direct_addressing_threshold).returns(1) + + connector = mock + connector.expects(:publish).with(m) + PluginManager.expects("[]").returns(connector) + + m.publish + m.type.should == :request + end + end + + describe "#create_reqid" do + it "should create a valid request id" do + m = Message.new("msg", "message", :agent => "rspec", :collective => "mc") + + SSL.expects(:uuid).returns("reqid") + + m.create_reqid.should == "reqid" + end + end + end +end