1
0
mirror of https://github.com/rs/zerolog synced 2026-06-08 17:13:30 +00:00
Files
rs-zerolog/event_test.go
T
Marc Brooks a64148507a Adds Objects field type to logger fields, events, and context (#736)
* Improve code coverage before adding Objects

* Refactor ErrorMarshal tests.

Added testing of the error marshal in Fields Reduce the duplication of the test data and make it clearer what the "wants" represent.

* Add Objects support to Fields, Event, Context

* Add support for Object Fields

Passed as []LogObjectMarshaler.

* Fixed syntax of ExampleContext_Objects

Seems 1.24 accepts variadic for arrays and understands covariance but 1.21 doesn't.

* Fix problem with []uint8 serialized as a Base64 string

* Better syntax in the example use of Objects
2025-12-18 18:50:34 +00:00

384 lines
8.5 KiB
Go

// +build !binary_log
package zerolog
import (
"bytes"
"errors"
"io"
"os"
"strings"
"testing"
)
type nilError struct{}
func (nilError) Error() string {
return ""
}
func TestEvent_AnErr(t *testing.T) {
tests := []struct {
name string
err error
want string
}{
{"nil", nil, `{}`},
{"error", errors.New("test"), `{"err":"test"}`},
{"nil interface", func() *nilError { return nil }(), `{}`},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var buf bytes.Buffer
e := newEvent(LevelWriterAdapter{&buf}, DebugLevel)
e.AnErr("err", tt.err)
_ = e.write()
if got, want := strings.TrimSpace(buf.String()), tt.want; got != want {
t.Errorf("Event.AnErr() = %v, want %v", got, want)
}
})
}
}
func TestEvent_writeWithNil(t *testing.T) {
var e *Event = nil
got := e.write()
var want *Event = nil
if got != nil {
t.Errorf("Event.write() = %v, want %v", got, want)
}
}
func TestEvent_ObjectWithNil(t *testing.T) {
var buf bytes.Buffer
e := newEvent(LevelWriterAdapter{&buf}, DebugLevel)
_ = e.Object("obj", nil)
_ = e.write()
want := `{"obj":null}`
got := strings.TrimSpace(buf.String())
if got != want {
t.Errorf("Event.Object() = %q, want %q", got, want)
}
}
func TestEvent_EmbedObjectWithNil(t *testing.T) {
var buf bytes.Buffer
e := newEvent(LevelWriterAdapter{&buf}, DebugLevel)
_ = e.EmbedObject(nil)
_ = e.write()
want := "{}"
got := strings.TrimSpace(buf.String())
if got != want {
t.Errorf("Event.EmbedObject() = %q, want %q", got, want)
}
}
func TestEvent_WithNilEvent(t *testing.T) {
// coverage for nil Event receiver for all types
var e *Event = nil
fixtures := makeFieldFixtures()
types := map[string]func() *Event{
"Array": func() *Event {
arr := Arr()
return e.Array("k", arr)
},
"Bool": func() *Event {
return e.Bool("k", fixtures.Bools[0])
},
"Bools": func() *Event {
return e.Bools("k", fixtures.Bools)
},
"Fields": func() *Event {
return e.Fields(fixtures)
},
"Int": func() *Event {
return e.Int("k", fixtures.Ints[0])
},
"Ints": func() *Event {
return e.Ints("k", fixtures.Ints)
},
"Int8": func() *Event {
return e.Int8("k", fixtures.Ints8[0])
},
"Ints8": func() *Event {
return e.Ints8("k", fixtures.Ints8)
},
"Int16": func() *Event {
return e.Int16("k", fixtures.Ints16[0])
},
"Ints16": func() *Event {
return e.Ints16("k", fixtures.Ints16)
},
"Int32": func() *Event {
return e.Int32("k", fixtures.Ints32[0])
},
"Ints32": func() *Event {
return e.Ints32("k", fixtures.Ints32)
},
"Int64": func() *Event {
return e.Int64("k", fixtures.Ints64[0])
},
"Ints64": func() *Event {
return e.Ints64("k", fixtures.Ints64)
},
"Uint": func() *Event {
return e.Uint("k", fixtures.Uints[0])
},
"Uints": func() *Event {
return e.Uints("k", fixtures.Uints)
},
"Uint8": func() *Event {
return e.Uint8("k", fixtures.Uints8[0])
},
"Uints8": func() *Event {
return e.Uints8("k", fixtures.Uints8)
},
"Uint16": func() *Event {
return e.Uint16("k", fixtures.Uints16[0])
},
"Uints16": func() *Event {
return e.Uints16("k", fixtures.Uints16)
},
"Uint32": func() *Event {
return e.Uint32("k", fixtures.Uints32[0])
},
"Uints32": func() *Event {
return e.Uints32("k", fixtures.Uints32)
},
"Uint64": func() *Event {
return e.Uint64("k", fixtures.Uints64[0])
},
"Uints64": func() *Event {
return e.Uints64("k", fixtures.Uints64)
},
"Float64": func() *Event {
return e.Float64("k", fixtures.Floats64[0])
},
"Floats64": func() *Event {
return e.Floats64("k", fixtures.Floats64)
},
"Float32": func() *Event {
return e.Float32("k", fixtures.Floats32[0])
},
"Floats32": func() *Event {
return e.Floats32("k", fixtures.Floats32)
},
"RawCBOR": func() *Event {
return e.RawCBOR("k", fixtures.RawCBOR)
},
"RawJSON": func() *Event {
return e.RawJSON("k", fixtures.RawJSONs[0])
},
"Str": func() *Event {
return e.Str("k", fixtures.Strings[0])
},
"Strs": func() *Event {
return e.Strs("k", fixtures.Strings)
},
"Stringers": func() *Event {
return e.Stringers("k", fixtures.Stringers)
},
"Err": func() *Event {
return e.Err(fixtures.Errs[0])
},
"Errs": func() *Event {
return e.Errs("k", fixtures.Errs)
},
"Ctx": func() *Event {
return e.Ctx(fixtures.Ctx)
},
"Time": func() *Event {
return e.Time("k", fixtures.Times[0])
},
"Times": func() *Event {
return e.Times("k", fixtures.Times)
},
"Dict": func() *Event {
d := Dict()
d.Str("greeting", "hello")
return e.Dict("k", d)
},
"Dur": func() *Event {
return e.Dur("k", fixtures.Durations[0])
},
"Durs": func() *Event {
return e.Durs("k", fixtures.Durations)
},
"Interface": func() *Event {
return e.Interface("k", fixtures.Interfaces[0])
},
"Interfaces": func() *Event {
return e.Interface("k", fixtures.Interfaces)
},
"Interface(Object)": func() *Event {
return e.Interface("k", fixtures.Objects[0])
},
"Interface(Objects)": func() *Event {
return e.Interface("k", fixtures.Objects)
},
"Object": func() *Event {
return e.Object("k", fixtures.Objects[0])
},
"Objects": func() *Event {
return e.Objects("k", fixtures.Objects)
},
"EmbedObject": func() *Event {
return e.EmbedObject(fixtures.Objects[0])
},
"Timestamp": func() *Event {
return e.Timestamp()
},
"IPAddr": func() *Event {
return e.IPAddr("k", fixtures.IPAddrs[0])
},
"IPAddrs": func() *Event {
return e.IPAddrs("k", fixtures.IPAddrs)
},
"IPPrefix": func() *Event {
return e.IPPrefix("k", fixtures.IPPfxs[0])
},
"IPPrefixes": func() *Event {
return e.IPPrefixes("k", fixtures.IPPfxs)
},
"MACAddr": func() *Event {
return e.MACAddr("k", fixtures.MACAddr)
},
"Type": func() *Event {
return e.Type("k", fixtures.Type)
},
"Caller": func() *Event {
return e.Caller(1)
},
"CallerSkip": func() *Event {
return e.CallerSkipFrame(2)
},
"Stack": func() *Event {
return e.Stack()
},
}
for name := range types {
f := types[name]
if got := f(); got != nil {
t.Errorf("Event.Bool() = %v, want %v", got, nil)
}
}
e.Send()
e.Msg("nothing")
e.Msgf("what %s", "nothing")
got := e.write()
if got != nil {
t.Errorf("Event.write() = %v, want %v", got, e)
}
called := false
e.MsgFunc(func() string {
called = true
return "called"
})
if called {
t.Errorf("Event.MsgFunc() should not be called on nil Event")
}
}
func TestEvent_MsgFunc(t *testing.T) {
var buf bytes.Buffer
e := newEvent(LevelWriterAdapter{&buf}, DebugLevel)
called := false
e.MsgFunc(func() string {
called = true
return "called"
})
if !called {
t.Errorf("Event.MsgFunc() was not called on non-nil Event")
}
want := `{"message":"called"}`
got := strings.TrimSpace(buf.String())
if got != want {
t.Errorf("Event.MsgFunc() = %q, want %q", got, want)
}
}
func TestEvent_DoneHandler(t *testing.T) {
e := newEvent(nil, InfoLevel)
// Set up a done handler to capture calls
var called bool
var capturedMsg string
e.done = func(msg string) {
called = true
capturedMsg = msg
}
// Trigger msg via Msg
e.Msg("test message")
// Assert the handler was called with the correct message
if !called {
t.Error("Done handler was not called")
}
if capturedMsg != "test message" {
t.Errorf("Expected message 'test message', got '%s'", capturedMsg)
}
}
type badLevelWriter struct {
err error
}
func (w *badLevelWriter) WriteLevel(level Level, p []byte) (n int, err error) {
return 0, w.err
}
func (w *badLevelWriter) Write(p []byte) (n int, err error) {
return 0, w.err
}
func TestEvent_Msg_ErrorHandlerNil(t *testing.T) {
// Save original ErrorHandler and restore after test
originalErrorHandler := ErrorHandler
ErrorHandler = nil
defer func() { ErrorHandler = originalErrorHandler }()
// Create a LevelWriter that always returns an error
mockWriter := &badLevelWriter{err: errors.New("write error")}
e := newEvent(mockWriter, InfoLevel)
if e == nil {
t.Fatal("Event should not be nil")
}
// Capture stderr
oldStderr := os.Stderr
r, w, err := os.Pipe()
if err != nil {
t.Fatal(err)
}
os.Stderr = w
// Call Msg to trigger write error
e.Msg("test message")
// Restore stderr and read captured output
w.Close()
os.Stderr = oldStderr
captured, err := io.ReadAll(r)
if err != nil {
t.Fatal(err)
}
// Assert the error message was printed to stderr
expected := "zerolog: could not write event: write error\n"
if string(captured) != expected {
t.Errorf("Expected stderr output %q, got %q", expected, string(captured))
}
}