
require File.expand_path('../setup', File.dirname(__FILE__))

module TestLogging
module TestLayouts

  class TestJson < Test::Unit::TestCase
    include LoggingTestCase

    def setup
      super
      @layout = Logging.layouts.json({})
      @levels = Logging::LEVELS
      @date_fmt = '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{6}(Z|[+-]\d{2}:\d{2})'
      Thread.current[:name] = nil
    end

    def test_initializer
      assert_raise(ArgumentError) {
        Logging.layouts.parseable.new :style => :foo
      }
    end

    def test_format
      event = Logging::LogEvent.new('ArrayLogger', @levels['info'],
                                    'log message', false)
      format = @layout.format(event)
      assert_match %r/"timestamp":"#@date_fmt"/, format
      assert_match %r/"level":"INFO"/, format
      assert_match %r/"logger":"ArrayLogger"/, format
      assert_match %r/"message":"log message"/, format

      event.data = [1, 2, 3, 4]
      format = @layout.format(event)
      assert_match %r/"timestamp":"#@date_fmt"/, format
      assert_match %r/"level":"INFO"/, format
      assert_match %r/"logger":"ArrayLogger"/, format
      assert_match %r/"message":\[1,2,3,4\]/, format

      event.level = @levels['debug']
      event.data = 'and another message'
      format = @layout.format(event)
      assert_match %r/"timestamp":"#@date_fmt"/, format
      assert_match %r/"level":"DEBUG"/, format
      assert_match %r/"logger":"ArrayLogger"/, format
      assert_match %r/"message":"and another message"/, format

      event.logger = 'Test'
      event.level = @levels['fatal']
      event.data = Exception.new
      format = @layout.format(event)
      assert_match %r/"timestamp":"#@date_fmt"/, format
      assert_match %r/"level":"FATAL"/, format
      assert_match %r/"logger":"Test"/, format
      assert_match %r/"message":\{(?:"(?:class|message)":"Exception",?){2}\}/, format
    end

    def test_items
      assert_equal %w[timestamp level logger message], @layout.items
    end

    def test_items_eq
      event = Logging::LogEvent.new('TestLogger', @levels['info'],
                                    ['log message'], false)

      @layout.items = %w[timestamp]
      assert_equal %w[timestamp], @layout.items
      assert_match %r/\{"timestamp":"#@date_fmt"\}\n/, @layout.format(event)

      # 'foo' is not a recognized item
      assert_raise(ArgumentError) {
        @layout.items = %w[timestamp logger foo]
      }
    end

    def test_items_all
      event = Logging::LogEvent.new('TestLogger', @levels['info'],
                                    'log message', false)
      event.file = 'test_file.rb'
      event.line = 123
      event.method = 'method_name'

      @layout.items = %w[logger]
      assert_equal %Q[{"logger":"TestLogger"}\n], @layout.format(event)

      @layout.items = %w[file]
      assert_equal %Q[{"file":"test_file.rb"}\n], @layout.format(event)

      @layout.items = %w[level]
      assert_equal %Q[{"level":"INFO"}\n], @layout.format(event)

      @layout.items = %w[line]
      assert_equal %Q[{"line":123}\n], @layout.format(event)

      @layout.items = %w[message]
      assert_equal %Q[{"message":"log message"}\n], @layout.format(event)

      @layout.items = %w[method]
      assert_equal %Q[{"method":"method_name"}\n], @layout.format(event)

      @layout.items = %w[hostname]
      assert_equal %Q[{"hostname":"#{Socket.gethostname}"}\n], @layout.format(event)

      @layout.items = %w[pid]
      assert_match %r/\A\{"pid":\d+\}\n\z/, @layout.format(event)

      @layout.items = %w[millis]
      assert_match %r/\A\{"millis":\d+\}\n\z/, @layout.format(event)

      @layout.items = %w[thread_id]
      assert_match %r/\A\{"thread_id":-?\d+\}\n\z/, @layout.format(event)

      @layout.items = %w[thread]
      assert_equal %Q[{"thread":null}\n], @layout.format(event)
      Thread.current[:name] = "Main"
      assert_equal %Q[{"thread":"Main"}\n], @layout.format(event)

      @layout.items = %w[mdc]
      assert_match %r/\A\{"mdc":\{\}\}\n\z/, @layout.format(event)

      @layout.items = %w[ndc]
      assert_match %r/\A\{"ndc":\[\]\}\n\z/, @layout.format(event)
    end

    def test_mdc_output
      event = Logging::LogEvent.new('TestLogger', @levels['info'],
                                    'log message', false)
      Logging.mdc['X-Session'] = '123abc'
      Logging.mdc['Cookie'] = 'monster'

      @layout.items = %w[timestamp level logger message mdc]

      format = @layout.format(event)
      assert_match %r/"timestamp":"#@date_fmt"/, format
      assert_match %r/"level":"INFO"/, format
      assert_match %r/"logger":"TestLogger"/, format
      assert_match %r/"message":"log message"/, format
      assert_match %r/"mdc":\{(?:(?:"X-Session":"123abc"|"Cookie":"monster"),?){2}\}/, format

      Logging.mdc.delete 'Cookie'
      format = @layout.format(event)
      assert_match %r/"mdc":\{"X-Session":"123abc"\}/, format
    end

    def test_ndc_output
      event = Logging::LogEvent.new('TestLogger', @levels['info'],
                                    'log message', false)
      Logging.ndc << 'context a'
      Logging.ndc << 'context b'

      @layout.items = %w[timestamp level logger message ndc]

      format = @layout.format(event)
      assert_match %r/"timestamp":"#@date_fmt"/, format
      assert_match %r/"level":"INFO"/, format
      assert_match %r/"logger":"TestLogger"/, format
      assert_match %r/"message":"log message"/, format
      assert_match %r/"ndc":\["context a","context b"\]/, format

      Logging.ndc.pop
      format = @layout.format(event)
      assert_match %r/"ndc":\["context a"\]/, format

      Logging.ndc.pop
      format = @layout.format(event)
      assert_match %r/"ndc":\[\]/, format
    end

  end  # class TestJson
end  # module TestLayouts
end  # module TestLogging

