1
2
3
4
5
6
7 package qlog
8
9 import (
10 "bytes"
11 "errors"
12 "fmt"
13 "log/slog"
14 "strings"
15 "sync"
16 "testing"
17 "time"
18 )
19
20 type testJSONOut struct {
21 bytes.Buffer
22 }
23
24 func (o *testJSONOut) Close() error { return nil }
25
26 func newTestJSONWriter() *jsonWriter {
27 return &jsonWriter{w: &testJSONOut{}}
28 }
29
30 func wantJSONRecord(t *testing.T, w *jsonWriter, want string) {
31 t.Helper()
32 want = "\x1e" + want + "\n"
33 got := w.w.(*testJSONOut).String()
34 if got != want {
35 t.Errorf("jsonWriter contains unexpected output\ngot: %q\nwant: %q", got, want)
36 }
37 }
38
39 func TestJSONWriterWriteConcurrentRecords(t *testing.T) {
40 w := newTestJSONWriter()
41 var wg sync.WaitGroup
42 for i := 0; i < 3; i++ {
43 wg.Add(1)
44 go func() {
45 defer wg.Done()
46 w.writeRecordStart()
47 w.writeInt64Field("field", 0)
48 w.writeRecordEnd()
49 }()
50 }
51 wg.Wait()
52 wantJSONRecord(t, w, strings.Join([]string{
53 `{"field":0}`,
54 `{"field":0}`,
55 `{"field":0}`,
56 }, "\n\x1e"))
57 }
58
59 func TestJSONWriterAttrs(t *testing.T) {
60 w := newTestJSONWriter()
61 w.writeRecordStart()
62 w.writeAttrsField("field", []slog.Attr{
63 slog.Any("any", errors.New("value")),
64 slog.Bool("bool", true),
65 slog.Duration("duration", 1*time.Second),
66 slog.Float64("float64", 1),
67 slog.Int64("int64", 1),
68 slog.String("string", "value"),
69 slog.Time("time", time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)),
70 slog.Uint64("uint64", 1),
71 slog.Group("group", "a", 1),
72 })
73 w.writeRecordEnd()
74 wantJSONRecord(t, w,
75 `{"field":{`+
76 `"any":"value",`+
77 `"bool":true,`+
78 `"duration":1000.000000,`+
79 `"float64":1,`+
80 `"int64":1,`+
81 `"string":"value",`+
82 `"time":946684800000.000000,`+
83 `"uint64":1,`+
84 `"group":{"a":1}`+
85 `}}`)
86 }
87
88 func TestJSONWriterObjectEmpty(t *testing.T) {
89 w := newTestJSONWriter()
90 w.writeRecordStart()
91 w.writeObjectField("field", func() {})
92 w.writeRecordEnd()
93 wantJSONRecord(t, w, `{"field":{}}`)
94 }
95
96 func TestJSONWriterObjectFields(t *testing.T) {
97 w := newTestJSONWriter()
98 w.writeRecordStart()
99 w.writeObjectField("field", func() {
100 w.writeStringField("a", "value")
101 w.writeInt64Field("b", 10)
102 })
103 w.writeRecordEnd()
104 wantJSONRecord(t, w, `{"field":{"a":"value","b":10}}`)
105 }
106
107 func TestJSONWriterRawField(t *testing.T) {
108 w := newTestJSONWriter()
109 w.writeRecordStart()
110 w.writeRawField("field", `[1]`)
111 w.writeRecordEnd()
112 wantJSONRecord(t, w, `{"field":[1]}`)
113 }
114
115 func TestJSONWriterBoolField(t *testing.T) {
116 w := newTestJSONWriter()
117 w.writeRecordStart()
118 w.writeBoolField("true", true)
119 w.writeBoolField("false", false)
120 w.writeRecordEnd()
121 wantJSONRecord(t, w, `{"true":true,"false":false}`)
122 }
123
124 func TestJSONWriterDurationField(t *testing.T) {
125 w := newTestJSONWriter()
126 w.writeRecordStart()
127 w.writeDurationField("field1", (10*time.Millisecond)+(2*time.Nanosecond))
128 w.writeDurationField("field2", -((10 * time.Millisecond) + (2 * time.Nanosecond)))
129 w.writeRecordEnd()
130 wantJSONRecord(t, w, `{"field1":10.000002,"field2":-10.000002}`)
131 }
132
133 func TestJSONWriterFloat64Field(t *testing.T) {
134 w := newTestJSONWriter()
135 w.writeRecordStart()
136 w.writeFloat64Field("field", 1.1)
137 w.writeRecordEnd()
138 wantJSONRecord(t, w, `{"field":1.1}`)
139 }
140
141 func TestJSONWriterInt64Field(t *testing.T) {
142 w := newTestJSONWriter()
143 w.writeRecordStart()
144 w.writeInt64Field("field", 1234)
145 w.writeRecordEnd()
146 wantJSONRecord(t, w, `{"field":1234}`)
147 }
148
149 func TestJSONWriterUint64Field(t *testing.T) {
150 w := newTestJSONWriter()
151 w.writeRecordStart()
152 w.writeUint64Field("field", 1234)
153 w.writeRecordEnd()
154 wantJSONRecord(t, w, `{"field":1234}`)
155 }
156
157 func TestJSONWriterStringField(t *testing.T) {
158 w := newTestJSONWriter()
159 w.writeRecordStart()
160 w.writeStringField("field", "value")
161 w.writeRecordEnd()
162 wantJSONRecord(t, w, `{"field":"value"}`)
163 }
164
165 func TestJSONWriterStringFieldEscaped(t *testing.T) {
166 w := newTestJSONWriter()
167 w.writeRecordStart()
168 w.writeStringField("field", "va\x00ue")
169 w.writeRecordEnd()
170 wantJSONRecord(t, w, `{"field":"va\u0000ue"}`)
171 }
172
173 func TestJSONWriterStringEscaping(t *testing.T) {
174 for c := 0; c <= 0xff; c++ {
175 w := newTestJSONWriter()
176 w.writeRecordStart()
177 w.writeStringField("field", string([]byte{byte(c)}))
178 w.writeRecordEnd()
179 var want string
180 if (c >= 0x20 && c <= 0x21) || (c >= 0x23 && c <= 0x5b) || (c >= 0x5d && c <= 0x7e) {
181 want = fmt.Sprintf(`%c`, c)
182 } else {
183 want = fmt.Sprintf(`\u%04x`, c)
184 }
185 wantJSONRecord(t, w, `{"field":"`+want+`"}`)
186 }
187 }
188
View as plain text