lib/semantic_logger/test/minitest.rb



module SemanticLogger
  module Test
    module Minitest
      # Returns [Array<SemanticLogger::Log>] the log events from Semantic Logger
      # captured whilst executing the supplied block.
      #
      # Notes:
      # - All log messages are returned regardless of the global default log level.
      def semantic_logger_events(deprecated_klass = nil, klass: deprecated_klass, silence: :trace, &block)
        logger = SemanticLogger::Test::CaptureLogEvents.new
        if klass
          klass.stub(:logger, logger, &block)
        elsif silence
          SemanticLogger.silence(silence) do
            SemanticLogger::Logger.stub(:processor, logger, &block)
          end
        else
          SemanticLogger::Logger.stub(:processor, logger, &block)
        end
        logger.events
      end

      # Verify a single log event has all the required attributes.
      def assert_semantic_logger_event(event, level: nil, name: nil, message: nil, message_includes: nil,
                                       payload: nil, payload_includes: nil,
                                       exception: nil, exception_includes: nil, backtrace: nil,
                                       thread_name: nil, tags: nil, named_tags: nil, context: nil,
                                       level_index: nil, duration: nil, time: nil,
                                       metric: nil, metric_amount: nil, dimensions: nil)
        assert event, "No log event occurred"

        assert_semantic_logger_entry(event, :message, message)
        assert_semantic_logger_entry(event, :name, name)
        assert_semantic_logger_entry(event, :level, level)
        assert_semantic_logger_entry(event, :thread_name, thread_name)
        assert_semantic_logger_entry(event, :tags, tags)
        assert_semantic_logger_entry(event, :named_tags, named_tags)
        assert_semantic_logger_entry(event, :context, context)
        assert_semantic_logger_entry(event, :metric, metric)
        assert_semantic_logger_entry(event, :metric_amount, metric_amount)
        assert_semantic_logger_entry(event, :dimensions, dimensions)
        assert_semantic_logger_entry(event, :level_index, level_index)
        assert_semantic_logger_entry(event, :duration, duration)
        assert_semantic_logger_entry(event, :time, time)
        assert_semantic_logger_entry(event, :exception, exception)
        assert_semantic_logger_entry(event, :backtrace, backtrace)
        assert_semantic_logger_entry(event, :payload, payload)

        if message_includes
          assert_includes(
            event.message,
            message_includes,
            -> { "Expected message to include '#{message_includes}' in log event #{event.inspect}" }
          )
        end

        if payload_includes
          payload_includes.each_pair do |key, expected|
            actual = event.payload[key]
            assert_semantic_logger_entry(event, "payload #{name}", expected, actual)
          end
        end

        return unless exception_includes

        payload_includes.each_pair do |key, expected|
          actual = event.exception.send(key)
          assert_semantic_logger_entry(event, "Exception #{name}", expected, actual)
        end
      end

      private

      def assert_semantic_logger_entry(event, name, expected, actual = event.send(name))
        return if expected.nil?

        case expected
        when :nil
          assert_nil actual, "Expected nil #{name} for log event: #{event.to_h.inspect}"
        when Class
          assert actual.is_a?(expected), lambda {
            "Type #{expected} expected for #{name} in log event: #{event.to_h.inspect}"
          }
        else
          assert_equal expected, actual, "Mismatched #{name} for log event: #{event.to_h.inspect}"
        end
      end
    end
  end
end