github.com/ssgreg/logf@v1.4.1/json_encoder_test.go (about)

     1  package logf
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"sync/atomic"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/stretchr/testify/assert"
    11  	"github.com/stretchr/testify/require"
    12  )
    13  
    14  type encoderTestCase struct {
    15  	Name   string
    16  	Entry  Entry
    17  	Golden string
    18  }
    19  
    20  var loggerID = int32(0)
    21  
    22  func newLoggerID() int32 {
    23  	atomic.AddInt32(&loggerID, 1)
    24  
    25  	return loggerID
    26  }
    27  
    28  func TestEncoder(t *testing.T) {
    29  	testCases := []encoderTestCase{
    30  		{
    31  			"Message",
    32  			Entry{
    33  				Text: "m",
    34  			},
    35  			`{"level":"error","ts":"0001-01-01T00:00:00Z","msg":"m"}` + "\n",
    36  		},
    37  		{
    38  			"LevelDebug",
    39  			Entry{
    40  				Level: LevelInfo,
    41  			},
    42  			`{"level":"info","ts":"0001-01-01T00:00:00Z","msg":""}` + "\n",
    43  		},
    44  		{
    45  			"LevelInfo",
    46  			Entry{
    47  				Level: LevelDebug,
    48  			},
    49  			`{"level":"debug","ts":"0001-01-01T00:00:00Z","msg":""}` + "\n",
    50  		},
    51  		{
    52  			"LevelWarn",
    53  			Entry{
    54  				Level: LevelWarn,
    55  			},
    56  			`{"level":"warn","ts":"0001-01-01T00:00:00Z","msg":""}` + "\n",
    57  		},
    58  		{
    59  			"LevelError",
    60  			Entry{
    61  				Level: LevelError,
    62  			},
    63  			`{"level":"error","ts":"0001-01-01T00:00:00Z","msg":""}` + "\n",
    64  		},
    65  		{
    66  			"LoggerName",
    67  			Entry{
    68  				LoggerName: "logger.name",
    69  			},
    70  			`{"level":"error","ts":"0001-01-01T00:00:00Z","logger":"logger.name","msg":""}` + "\n",
    71  		},
    72  		{
    73  			"LoggerName",
    74  			Entry{
    75  				Caller: EntryCaller{
    76  					File:      "/a/b/c/f.go",
    77  					Line:      6,
    78  					Specified: true,
    79  				},
    80  			},
    81  			`{"level":"error","ts":"0001-01-01T00:00:00Z","msg":"","caller":"c/f.go:6"}` + "\n",
    82  		},
    83  		{
    84  			"FieldsNumbers",
    85  			Entry{
    86  				Fields: []Field{
    87  					Bool("bool", true),
    88  					Int("int", 42), Int64("int64", 42), Int32("int32", 42), Int16("int16", 42), Int8("int8", 42),
    89  					Uint("uint", 42), Uint64("uint64", 42), Uint32("uint32", 42), Uint16("uint16", 42), Uint8("uint8", 42),
    90  					Float64("float64", 4.2), Float32("float32", 4.2),
    91  				},
    92  			},
    93  			`{"level":"error","ts":"0001-01-01T00:00:00Z","msg":"","bool":true,"int":42,"int64":42,"int32":42,"int16":42,"int8":42,"uint":42,"uint64":42,"uint32":42,"uint16":42,"uint8":42,"float64":4.2,"float32":4.2}` + "\n",
    94  		},
    95  		{
    96  			"FieldsSlicesWithNumbers",
    97  			Entry{
    98  				Fields: []Field{
    99  					ConstBools("bools", []bool{true}),
   100  					ConstInts("ints", []int{42}), ConstInts64("ints64", []int64{42}), ConstInts32("ints32", []int32{42}), ConstInts16("ints16", []int16{42}), ConstInts8("ints8", []int8{42}),
   101  					ConstUints("uints", []uint{42}), ConstUints64("uints64", []uint64{42}), ConstUints32("uints32", []uint32{42}), ConstUints16("uints16", []uint16{42}), ConstUints8("uints8", []uint8{42}),
   102  					ConstFloats64("floats64", []float64{4.2}), ConstFloats32("floats32", []float32{4.2}),
   103  				},
   104  			},
   105  			`{"level":"error","ts":"0001-01-01T00:00:00Z","msg":"","bools":[true],"ints":[42],"ints64":[42],"ints32":[42],"ints16":[42],"ints8":[42],"uints":[42],"uints64":[42],"uints32":[42],"uints16":[42],"uints8":[42],"floats64":[4.2],"floats32":[4.2]}` + "\n",
   106  		},
   107  		{
   108  			"FieldsDuration",
   109  			Entry{
   110  				Fields: []Field{
   111  					Duration("duration", time.Second),
   112  					ConstDurations("durations", []time.Duration{time.Second}),
   113  				},
   114  			},
   115  			`{"level":"error","ts":"0001-01-01T00:00:00Z","msg":"","duration":"1s","durations":["1s"]}` + "\n",
   116  		},
   117  		{
   118  			"FieldsTime",
   119  			Entry{
   120  				Fields: []Field{
   121  					Time("time", time.Unix(320836234, 0).UTC()),
   122  				},
   123  			},
   124  			`{"level":"error","ts":"0001-01-01T00:00:00Z","msg":"","time":"1980-03-02T09:10:34Z"}` + "\n",
   125  		},
   126  		{
   127  			"FieldsArray",
   128  			Entry{
   129  				Fields: []Field{
   130  					Array("array", &testArrayEncoder{}),
   131  				},
   132  			},
   133  			`{"level":"error","ts":"0001-01-01T00:00:00Z","msg":"","array":[42]}` + "\n",
   134  		},
   135  		{
   136  			"FieldsObject",
   137  			Entry{
   138  				Fields: []Field{
   139  					Object("object", &testObjectEncoder{}),
   140  				},
   141  			},
   142  			`{"level":"error","ts":"0001-01-01T00:00:00Z","msg":"","object":{"username":"username","code":42}}` + "\n",
   143  		},
   144  		{
   145  			"FieldsError",
   146  			Entry{
   147  				Fields: []Field{
   148  					Error(&verboseError{"short", "verbose"}),
   149  				},
   150  			},
   151  			`{"level":"error","ts":"0001-01-01T00:00:00Z","msg":"","error":"short","error.verbose":"verbose"}` + "\n",
   152  		},
   153  		{
   154  			"FieldsBytes",
   155  			Entry{
   156  				Fields: []Field{
   157  					ConstBytes("bytes", []byte{0x42}),
   158  				},
   159  			},
   160  			`{"level":"error","ts":"0001-01-01T00:00:00Z","msg":"","bytes":"Qg=="}` + "\n",
   161  		},
   162  		{
   163  			"FieldsAny",
   164  			Entry{
   165  				Fields: []Field{
   166  					Any("any", &struct{ Field string }{Field: "42"}),
   167  				},
   168  			},
   169  			`{"level":"error","ts":"0001-01-01T00:00:00Z","msg":"","any":{"Field":"42"}}` + "\n",
   170  		},
   171  		{
   172  			"FieldsDerivedFields",
   173  			Entry{
   174  				DerivedFields: []Field{
   175  					Int("int", 42),
   176  				},
   177  			},
   178  			`{"level":"error","ts":"0001-01-01T00:00:00Z","msg":"","int":42}` + "\n",
   179  		},
   180  		{
   181  			"FieldsDerivedFieldsFirst",
   182  			Entry{
   183  				DerivedFields: []Field{
   184  					Int("int", 42),
   185  				},
   186  				Fields: []Field{
   187  					String("string", "42"),
   188  				},
   189  			},
   190  			`{"level":"error","ts":"0001-01-01T00:00:00Z","msg":"","int":42,"string":"42"}` + "\n",
   191  		},
   192  	}
   193  
   194  	enc := NewJSONEncoder.Default()
   195  
   196  	for _, tc := range testCases {
   197  		t.Run(tc.Name, func(t *testing.T) {
   198  			// Setup skipped fields (skipped for short test case description).
   199  			tc.Entry.LoggerID = newLoggerID()
   200  
   201  			b := NewBuffer()
   202  			enc.Encode(b, tc.Entry)
   203  			require.EqualValues(t, tc.Golden, b.String())
   204  
   205  			// Check for correct json.
   206  			var a map[string]interface{}
   207  			require.NoError(t, json.NewDecoder(bytes.NewBuffer(b.Bytes())).Decode(&a), "generated json expected to be parsed by native golang json encoder")
   208  		})
   209  	}
   210  }
   211  
   212  func TestEscapeString(t *testing.T) {
   213  	testCases := []struct {
   214  		golden string
   215  		source string
   216  	}{
   217  		{`кириллица`, "кириллица"},
   218  		{`not<escape>html`, `not<escape>html`},
   219  		{`badtext\ufffd`, "badtext\xc5"},
   220  		{`ошибка\ufffdошибка`, "ошибка\xc5ошибка"},
   221  		{`测试`, "测试"},
   222  		{`测\ufffd试`, "测\xc5试"},
   223  		{`\u0008\\\r\n\t\"`, "\b\\\r\n\t\""},
   224  	}
   225  
   226  	for _, tc := range testCases {
   227  		b := NewBuffer()
   228  		assert.NoError(t, EscapeString(b, tc.source))
   229  		assert.Equal(t, tc.golden, b.String())
   230  	}
   231  }
   232  
   233  func TestEscapeByteString(t *testing.T) {
   234  	testCases := []struct {
   235  		golden string
   236  		source string
   237  	}{
   238  		{`кириллица`, "кириллица"},
   239  		{`not<escape>html`, `not<escape>html`},
   240  		{`测试`, "测试"},
   241  		{`\u0008\\\r\n\t\"`, "\b\\\r\n\t\""},
   242  	}
   243  
   244  	for _, tc := range testCases {
   245  		b := NewBuffer()
   246  		assert.NoError(t, EscapeByteString(b, []byte(tc.source)))
   247  		assert.Equal(t, tc.golden, b.String())
   248  	}
   249  }
   250  
   251  func TestEncoderFactory(t *testing.T) {
   252  	b := NewBuffer()
   253  	ef := NewJSONTypeEncoderFactory.Default()
   254  	te := ef.TypeEncoder(b)
   255  
   256  	te.EncodeTypeString("42")
   257  	assert.Equal(t, `"42"`, b.String())
   258  }