github.com/Laisky/zap@v1.27.0/zapcore/field_test.go (about)

     1  // Copyright (c) 2016 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package zapcore_test
    22  
    23  import (
    24  	"errors"
    25  	"fmt"
    26  	"math"
    27  	"net/url"
    28  	"testing"
    29  	"time"
    30  
    31  	"github.com/Laisky/zap"
    32  	"github.com/stretchr/testify/assert"
    33  	"github.com/stretchr/testify/require"
    34  	//revive:disable:dot-imports
    35  	. "github.com/Laisky/zap/zapcore"
    36  )
    37  
    38  type users int
    39  
    40  func (u users) String() string {
    41  	return fmt.Sprintf("%d users", int(u))
    42  }
    43  
    44  func (u users) MarshalLogObject(enc ObjectEncoder) error {
    45  	if int(u) < 0 {
    46  		return errors.New("too few users")
    47  	}
    48  	enc.AddInt("users", int(u))
    49  	return nil
    50  }
    51  
    52  func (u users) MarshalLogArray(enc ArrayEncoder) error {
    53  	if int(u) < 0 {
    54  		return errors.New("too few users")
    55  	}
    56  	for i := 0; i < int(u); i++ {
    57  		enc.AppendString("user")
    58  	}
    59  	return nil
    60  }
    61  
    62  type obj struct {
    63  	kind int
    64  }
    65  
    66  func (o *obj) String() string {
    67  	if o == nil {
    68  		return "nil obj"
    69  	}
    70  
    71  	if o.kind == 1 {
    72  		panic("panic with string")
    73  	} else if o.kind == 2 {
    74  		panic(errors.New("panic with error"))
    75  	} else if o.kind == 3 {
    76  		// panic with an arbitrary object that causes a panic itself
    77  		// when being converted to a string
    78  		panic((*url.URL)(nil))
    79  	}
    80  
    81  	return "obj"
    82  }
    83  
    84  type errObj struct {
    85  	kind   int
    86  	errMsg string
    87  }
    88  
    89  func (eobj *errObj) Error() string {
    90  	if eobj.kind == 1 {
    91  		panic("panic in Error() method")
    92  	}
    93  	return eobj.errMsg
    94  }
    95  
    96  func TestUnknownFieldType(t *testing.T) {
    97  	unknown := Field{Key: "k", String: "foo"}
    98  	assert.Equal(t, UnknownType, unknown.Type, "Expected zero value of FieldType to be UnknownType.")
    99  	assert.Panics(t, func() {
   100  		unknown.AddTo(NewMapObjectEncoder())
   101  	}, "Expected using a field with unknown type to panic.")
   102  }
   103  
   104  func TestFieldAddingError(t *testing.T) {
   105  	var empty interface{}
   106  	tests := []struct {
   107  		t     FieldType
   108  		iface interface{}
   109  		want  interface{}
   110  		err   string
   111  	}{
   112  		{t: ArrayMarshalerType, iface: users(-1), want: []interface{}{}, err: "too few users"},
   113  		{t: ObjectMarshalerType, iface: users(-1), want: map[string]interface{}{}, err: "too few users"},
   114  		{t: InlineMarshalerType, iface: users(-1), want: nil, err: "too few users"},
   115  		{t: StringerType, iface: obj{}, want: empty, err: "PANIC=interface conversion: zapcore_test.obj is not fmt.Stringer: missing method String"},
   116  		{t: StringerType, iface: &obj{1}, want: empty, err: "PANIC=panic with string"},
   117  		{t: StringerType, iface: &obj{2}, want: empty, err: "PANIC=panic with error"},
   118  		{t: StringerType, iface: &obj{3}, want: empty, err: "PANIC=<nil>"},
   119  		{t: ErrorType, iface: &errObj{kind: 1}, want: empty, err: "PANIC=panic in Error() method"},
   120  	}
   121  	for _, tt := range tests {
   122  		f := Field{Key: "k", Interface: tt.iface, Type: tt.t}
   123  		enc := NewMapObjectEncoder()
   124  		assert.NotPanics(t, func() { f.AddTo(enc) }, "Unexpected panic when adding fields returns an error.")
   125  		assert.Equal(t, tt.want, enc.Fields["k"], "On error, expected zero value in field.Key.")
   126  		assert.Equal(t, tt.err, enc.Fields["kError"], "Expected error message in log context.")
   127  	}
   128  }
   129  
   130  func TestFields(t *testing.T) {
   131  	tests := []struct {
   132  		t     FieldType
   133  		i     int64
   134  		s     string
   135  		iface interface{}
   136  		want  interface{}
   137  	}{
   138  		{t: ArrayMarshalerType, iface: users(2), want: []interface{}{"user", "user"}},
   139  		{t: ObjectMarshalerType, iface: users(2), want: map[string]interface{}{"users": 2}},
   140  		{t: BoolType, i: 0, want: false},
   141  		{t: ByteStringType, iface: []byte("foo"), want: "foo"},
   142  		{t: Complex128Type, iface: 1 + 2i, want: 1 + 2i},
   143  		{t: Complex64Type, iface: complex64(1 + 2i), want: complex64(1 + 2i)},
   144  		{t: DurationType, i: 1000, want: time.Microsecond},
   145  		{t: Float64Type, i: int64(math.Float64bits(3.14)), want: 3.14},
   146  		{t: Float32Type, i: int64(math.Float32bits(3.14)), want: float32(3.14)},
   147  		{t: Int64Type, i: 42, want: int64(42)},
   148  		{t: Int32Type, i: 42, want: int32(42)},
   149  		{t: Int16Type, i: 42, want: int16(42)},
   150  		{t: Int8Type, i: 42, want: int8(42)},
   151  		{t: StringType, s: "foo", want: "foo"},
   152  		{t: TimeType, i: 1000, iface: time.UTC, want: time.Unix(0, 1000).In(time.UTC)},
   153  		{t: TimeType, i: 1000, want: time.Unix(0, 1000)},
   154  		{t: Uint64Type, i: 42, want: uint64(42)},
   155  		{t: Uint32Type, i: 42, want: uint32(42)},
   156  		{t: Uint16Type, i: 42, want: uint16(42)},
   157  		{t: Uint8Type, i: 42, want: uint8(42)},
   158  		{t: UintptrType, i: 42, want: uintptr(42)},
   159  		{t: ReflectType, iface: users(2), want: users(2)},
   160  		{t: NamespaceType, want: map[string]interface{}{}},
   161  		{t: StringerType, iface: users(2), want: "2 users"},
   162  		{t: StringerType, iface: &obj{}, want: "obj"},
   163  		{t: StringerType, iface: (*obj)(nil), want: "nil obj"},
   164  		{t: SkipType, want: interface{}(nil)},
   165  		{t: StringerType, iface: (*url.URL)(nil), want: "<nil>"},
   166  		{t: StringerType, iface: (*users)(nil), want: "<nil>"},
   167  		{t: ErrorType, iface: (*errObj)(nil), want: "<nil>"},
   168  	}
   169  
   170  	for _, tt := range tests {
   171  		enc := NewMapObjectEncoder()
   172  		f := Field{Key: "k", Type: tt.t, Integer: tt.i, Interface: tt.iface, String: tt.s}
   173  		f.AddTo(enc)
   174  		assert.Equal(t, tt.want, enc.Fields["k"], "Unexpected output from field %+v.", f)
   175  
   176  		delete(enc.Fields, "k")
   177  		assert.Equal(t, 0, len(enc.Fields), "Unexpected extra fields present.")
   178  
   179  		assert.True(t, f.Equals(f), "Field does not equal itself")
   180  	}
   181  }
   182  
   183  func TestInlineMarshaler(t *testing.T) {
   184  	enc := NewMapObjectEncoder()
   185  
   186  	topLevelStr := Field{Key: "k", Type: StringType, String: "s"}
   187  	topLevelStr.AddTo(enc)
   188  
   189  	inlineObj := Field{Key: "ignored", Type: InlineMarshalerType, Interface: users(10)}
   190  	inlineObj.AddTo(enc)
   191  
   192  	nestedObj := Field{Key: "nested", Type: ObjectMarshalerType, Interface: users(11)}
   193  	nestedObj.AddTo(enc)
   194  
   195  	assert.Equal(t, map[string]interface{}{
   196  		"k":     "s",
   197  		"users": 10,
   198  		"nested": map[string]interface{}{
   199  			"users": 11,
   200  		},
   201  	}, enc.Fields)
   202  }
   203  
   204  func TestEquals(t *testing.T) {
   205  	// Values outside the UnixNano range were encoded incorrectly (#737, #803).
   206  	timeOutOfRangeHigh := time.Unix(0, math.MaxInt64).Add(time.Nanosecond)
   207  	timeOutOfRangeLow := time.Unix(0, math.MinInt64).Add(-time.Nanosecond)
   208  	timeOutOfRangeHighNano := time.Unix(0, timeOutOfRangeHigh.UnixNano())
   209  	timeOutOfRangeLowNano := time.Unix(0, timeOutOfRangeLow.UnixNano())
   210  	require.False(t, timeOutOfRangeHigh.Equal(timeOutOfRangeHighNano), "should be different as value is >  UnixNano range")
   211  	require.False(t, timeOutOfRangeHigh.Equal(timeOutOfRangeHighNano), "should be different as value is <  UnixNano range")
   212  
   213  	tests := []struct {
   214  		a, b Field
   215  		want bool
   216  	}{
   217  		{
   218  			a:    zap.Int16("a", 1),
   219  			b:    zap.Int32("a", 1),
   220  			want: false,
   221  		},
   222  		{
   223  			a:    zap.String("k", "a"),
   224  			b:    zap.String("k", "a"),
   225  			want: true,
   226  		},
   227  		{
   228  			a:    zap.String("k", "a"),
   229  			b:    zap.String("k2", "a"),
   230  			want: false,
   231  		},
   232  		{
   233  			a:    zap.String("k", "a"),
   234  			b:    zap.String("k", "b"),
   235  			want: false,
   236  		},
   237  		{
   238  			a:    zap.Time("k", time.Unix(1000, 1000)),
   239  			b:    zap.Time("k", time.Unix(1000, 1000)),
   240  			want: true,
   241  		},
   242  		{
   243  			a:    zap.Time("k", time.Unix(1000, 1000).In(time.UTC)),
   244  			b:    zap.Time("k", time.Unix(1000, 1000).In(time.FixedZone("TEST", -8))),
   245  			want: false,
   246  		},
   247  		{
   248  			a:    zap.Time("k", timeOutOfRangeLow),
   249  			b:    zap.Time("k", timeOutOfRangeLowNano),
   250  			want: false,
   251  		},
   252  		{
   253  			a:    zap.Time("k", timeOutOfRangeHigh),
   254  			b:    zap.Time("k", timeOutOfRangeHighNano),
   255  			want: false,
   256  		},
   257  		{
   258  			a:    zap.Time("k", time.Unix(1000, 1000)),
   259  			b:    zap.Time("k", time.Unix(1000, 2000)),
   260  			want: false,
   261  		},
   262  		{
   263  			a:    zap.Binary("k", []byte{1, 2}),
   264  			b:    zap.Binary("k", []byte{1, 2}),
   265  			want: true,
   266  		},
   267  		{
   268  			a:    zap.Binary("k", []byte{1, 2}),
   269  			b:    zap.Binary("k", []byte{1, 3}),
   270  			want: false,
   271  		},
   272  		{
   273  			a:    zap.ByteString("k", []byte("abc")),
   274  			b:    zap.ByteString("k", []byte("abc")),
   275  			want: true,
   276  		},
   277  		{
   278  			a:    zap.ByteString("k", []byte("abc")),
   279  			b:    zap.ByteString("k", []byte("abd")),
   280  			want: false,
   281  		},
   282  		{
   283  			a:    zap.Ints("k", []int{1, 2}),
   284  			b:    zap.Ints("k", []int{1, 2}),
   285  			want: true,
   286  		},
   287  		{
   288  			a:    zap.Ints("k", []int{1, 2}),
   289  			b:    zap.Ints("k", []int{1, 3}),
   290  			want: false,
   291  		},
   292  		{
   293  			a:    zap.Object("k", users(10)),
   294  			b:    zap.Object("k", users(10)),
   295  			want: true,
   296  		},
   297  		{
   298  			a:    zap.Object("k", users(10)),
   299  			b:    zap.Object("k", users(20)),
   300  			want: false,
   301  		},
   302  		{
   303  			a:    zap.Any("k", map[string]string{"a": "b"}),
   304  			b:    zap.Any("k", map[string]string{"a": "b"}),
   305  			want: true,
   306  		},
   307  		{
   308  			a:    zap.Any("k", map[string]string{"a": "b"}),
   309  			b:    zap.Any("k", map[string]string{"a": "d"}),
   310  			want: false,
   311  		},
   312  		{
   313  			a:    zap.Dict("k", zap.String("a", "b")),
   314  			b:    zap.Dict("k", zap.String("a", "b")),
   315  			want: true,
   316  		},
   317  		{
   318  			a:    zap.Dict("k", zap.String("a", "b")),
   319  			b:    zap.Dict("k", zap.String("a", "d")),
   320  			want: false,
   321  		},
   322  	}
   323  
   324  	for _, tt := range tests {
   325  		assert.Equal(t, tt.want, tt.a.Equals(tt.b), "a.Equals(b) a: %#v b: %#v", tt.a, tt.b)
   326  		assert.Equal(t, tt.want, tt.b.Equals(tt.a), "b.Equals(a) a: %#v b: %#v", tt.a, tt.b)
   327  	}
   328  }