# encoding: utf-8 require 'spec_helper' describe 'Bugsnag::MongoBreadcrumbSubscriber', :order => :defined do before do unless defined?(::Mongo) @mocked_mongo = true module ::Mongo module Monitoring COMMAND = 'Command' module Global end end end module Kernel alias_method :old_require, :require def require(path) old_require(path) unless path == 'mongo' end end end end it "should subscribe to the mongo monitoring service" do expect(::Mongo::Monitoring::Global).to receive(:subscribe) do |command, subscriber| expect(command).to eq(::Mongo::Monitoring::COMMAND) expect(subscriber).to be_an_instance_of(::Bugsnag::MongoBreadcrumbSubscriber) end load './lib/bugsnag/integrations/mongo.rb' end context "with the module loaded" do before do allow(::Mongo::Monitoring::Global).to receive(:subscribe) require './lib/bugsnag/integrations/mongo' end let(:subscriber) { Bugsnag::MongoBreadcrumbSubscriber.new } describe "#started" do it "calls #leave_command with the event" do event = double expect(subscriber).to receive(:leave_command).with(event) subscriber.started(event) end end describe "#succeeded" do it "calls #leave_mongo_breadcrumb with the event_name and event" do event = double expect(subscriber).to receive(:leave_mongo_breadcrumb).with("succeeded", event) subscriber.succeeded(event) end end describe "#failed" do it "calls #leave_mongo_breadcrumb with the event_name and event" do event = double expect(subscriber).to receive(:leave_mongo_breadcrumb).with("failed", event) subscriber.failed(event) end end describe "#leave_mongo_breadcrumb" do let(:event) { double( :command_name => "command", :database_name => "database", :operation_id => "1234567890", :request_id => "123456", :duration => "123.456" ) } let(:event_name) { "event_name" } it "leaves a breadcrumb with relevant meta_data, message, type, and automatic notation" do expect(Bugsnag).to receive(:leave_breadcrumb).with( "Mongo query #{event_name}", { :event_name => "mongo.#{event_name}", :command_name => "command", :database_name => "database", :operation_id => "1234567890", :request_id => "123456", :duration => "123.456" }, "process", :auto ) subscriber.send(:leave_mongo_breadcrumb, event_name, event) end it "adds message data if present" do allow(event).to receive(:message).and_return("This is a message") expect(Bugsnag).to receive(:leave_breadcrumb).with( "Mongo query #{event_name}", { :event_name => "mongo.#{event_name}", :command_name => "command", :database_name => "database", :operation_id => "1234567890", :request_id => "123456", :duration => "123.456", :message => "This is a message" }, "process", :auto ) subscriber.send(:leave_mongo_breadcrumb, event_name, event) end context "command data is present" do let(:command) { { "command" => "collection_name_command", "collection" => "collection_name_getMore", "filter" => nil } } it "adds the collection name" do expect(subscriber).to receive(:pop_command).with("123456").and_return(command) expect(Bugsnag).to receive(:leave_breadcrumb).with( "Mongo query #{event_name}", { :event_name => "mongo.#{event_name}", :command_name => "command", :database_name => "database", :operation_id => "1234567890", :request_id => "123456", :duration => "123.456", :collection => "collection_name_command" }, "process", :auto ) subscriber.send(:leave_mongo_breadcrumb, event_name, event) end it "adds the correct collection name for 'getMore' commands" do allow(event).to receive(:command_name).and_return("getMore") expect(subscriber).to receive(:pop_command).with("123456").and_return(command) expect(Bugsnag).to receive(:leave_breadcrumb).with( "Mongo query #{event_name}", { :event_name => "mongo.#{event_name}", :command_name => "getMore", :database_name => "database", :operation_id => "1234567890", :request_id => "123456", :duration => "123.456", :collection => "collection_name_getMore" }, "process", :auto ) subscriber.send(:leave_mongo_breadcrumb, event_name, event) end it "adds a JSON string of filter data" do command["filter"] = {"a" => 1, "b" => 2, "$or" => [{"c" => 3}, {"d" => 4}]} expect(subscriber).to receive(:pop_command).with("123456").and_return(command) expect(Bugsnag).to receive(:leave_breadcrumb).with( "Mongo query #{event_name}", { :event_name => "mongo.#{event_name}", :command_name => "command", :database_name => "database", :operation_id => "1234567890", :request_id => "123456", :duration => "123.456", :collection => "collection_name_command", :filter => '{"a":"?","b":"?","$or":[{"c":"?"},{"d":"?"}]}' }, "process", :auto ) subscriber.send(:leave_mongo_breadcrumb, event_name, event) end end end describe "#sanitize_filter_hash" do it "calls into #sanitize_filter_value with the value from each {k,v} pair" do expect(subscriber.send(:sanitize_filter_hash, {:a => 1, :b => 2})).to eq({:a => '?', :b => '?'}) end end describe "#sanitize_filter_value" do it "returns '?' for strings, numbers, booleans, and nil" do expect(subscriber.send(:sanitize_filter_value, 523, 0)).to eq('?') expect(subscriber.send(:sanitize_filter_value, "string", 0)).to eq('?') expect(subscriber.send(:sanitize_filter_value, true, 0)).to eq('?') expect(subscriber.send(:sanitize_filter_value, nil, 0)).to eq('?') end it "is recursive and iterative for array values" do expect(subscriber.send(:sanitize_filter_value, [1, [2, [3]]], 0)).to eq(['?', ['?', ['?']]]) end it "calls #sanitize_filter_hash for hash values" do expect(subscriber).to receive(:sanitize_filter_hash).with({:a => 1}, 1) subscriber.send(:sanitize_filter_value, {:a => 1}, 0) end it "returns [MAX_FILTER_DEPTH_REACHED] if the filter depth is exceeded" do expect(subscriber.send(:sanitize_filter_value, 1, 4)).to eq('[MAX_FILTER_DEPTH_REACHED]') end end describe "#leave_command" do it "extracts and stores the command by request_id" do request_id = "123456" command = "this is a command string" event = double(:command => command, :request_id => request_id) subscriber.send(:leave_command, event) command_hash = Bugsnag.configuration.request_data[Bugsnag::MongoBreadcrumbSubscriber::MONGO_COMMAND_KEY] expect(command_hash[request_id]).to eq(command) end end describe "#pop_command" do let(:request_id) { "123456" } let(:command) { "this is a command string" } before do event = double(:command => command, :request_id => request_id) subscriber.send(:leave_command, event) end it "returns the command given a request_id" do expect(subscriber.send(:pop_command, request_id)).to eq(command) end it "removes the command from the request_data" do subscriber.send(:pop_command, request_id) command_hash = Bugsnag.configuration.request_data[Bugsnag::MongoBreadcrumbSubscriber::MONGO_COMMAND_KEY] expect(command_hash).not_to have_key(request_id) end it "returns nil if the request_id is not found" do expect(subscriber.send(:pop_command, "09876")).to be_nil end end describe "#event_commands" do it "returns a hash" do expect(subscriber.send(:event_commands)).to be_a(Hash) end it "is stored in request data" do subscriber.send(:event_commands)[:key] = "value" command_hash = Bugsnag.configuration.request_data[Bugsnag::MongoBreadcrumbSubscriber::MONGO_COMMAND_KEY] expect(command_hash[:key]).to eq("value") end end end after do Object.send(:remove_const, :Mongo) if @mocked_mongo module Kernel alias_method :require, :old_require end end end