--- /dev/null
+#!/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