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 TestJSONWriterAttrEmpty(t *testing.T) {
89 w := newTestJSONWriter()
90 w.writeRecordStart()
91 var a slog.Attr
92 w.writeAttr(a)
93 w.writeRecordEnd()
94 wantJSONRecord(t, w, `{}`)
95 }
96
97 func TestJSONWriterObjectEmpty(t *testing.T) {
98 w := newTestJSONWriter()
99 w.writeRecordStart()
100 w.writeObjectField("field", func() {})
101 w.writeRecordEnd()
102 wantJSONRecord(t, w, `{"field":{}}`)
103 }
104
105 func TestJSONWriterObjectFields(t *testing.T) {
106 w := newTestJSONWriter()
107 w.writeRecordStart()
108 w.writeObjectField("field", func() {
109 w.writeStringField("a", "value")
110 w.writeInt64Field("b", 10)
111 })
112 w.writeRecordEnd()
113 wantJSONRecord(t, w, `{"field":{"a":"value","b":10}}`)
114 }
115
116 func TestJSONWriterRawField(t *testing.T) {
117 w := newTestJSONWriter()
118 w.writeRecordStart()
119 w.writeRawField("field", `[1]`)
120 w.writeRecordEnd()
121 wantJSONRecord(t, w, `{"field":[1]}`)
122 }
123
124 func TestJSONWriterBoolField(t *testing.T) {
125 w := newTestJSONWriter()
126 w.writeRecordStart()
127 w.writeBoolField("true", true)
128 w.writeBoolField("false", false)
129 w.writeRecordEnd()
130 wantJSONRecord(t, w, `{"true":true,"false":false}`)
131 }
132
133 func TestJSONWriterDurationField(t *testing.T) {
134 w := newTestJSONWriter()
135 w.writeRecordStart()
136 w.writeDurationField("field1", (10*time.Millisecond)+(2*time.Nanosecond))
137 w.writeDurationField("field2", -((10 * time.Millisecond) + (2 * time.Nanosecond)))
138 w.writeRecordEnd()
139 wantJSONRecord(t, w, `{"field1":10.000002,"field2":-10.000002}`)
140 }
141
142 func TestJSONWriterFloat64Field(t *testing.T) {
143 w := newTestJSONWriter()
144 w.writeRecordStart()
145 w.writeFloat64Field("field", 1.1)
146 w.writeRecordEnd()
147 wantJSONRecord(t, w, `{"field":1.1}`)
148 }
149
150 func TestJSONWriterInt64Field(t *testing.T) {
151 w := newTestJSONWriter()
152 w.writeRecordStart()
153 w.writeInt64Field("field", 1234)
154 w.writeRecordEnd()
155 wantJSONRecord(t, w, `{"field":1234}`)
156 }
157
158 func TestJSONWriterUint64Field(t *testing.T) {
159 w := newTestJSONWriter()
160 w.writeRecordStart()
161 w.writeUint64Field("field", 1234)
162 w.writeRecordEnd()
163 wantJSONRecord(t, w, `{"field":1234}`)
164 }
165
166 func TestJSONWriterStringField(t *testing.T) {
167 w := newTestJSONWriter()
168 w.writeRecordStart()
169 w.writeStringField("field", "value")
170 w.writeRecordEnd()
171 wantJSONRecord(t, w, `{"field":"value"}`)
172 }
173
174 func TestJSONWriterStringFieldEscaped(t *testing.T) {
175 w := newTestJSONWriter()
176 w.writeRecordStart()
177 w.writeStringField("field", "va\x00ue")
178 w.writeRecordEnd()
179 wantJSONRecord(t, w, `{"field":"va\u0000ue"}`)
180 }
181
182 func TestJSONWriterStringEscaping(t *testing.T) {
183 for c := 0; c <= 0xff; c++ {
184 w := newTestJSONWriter()
185 w.writeRecordStart()
186 w.writeStringField("field", string([]byte{byte(c)}))
187 w.writeRecordEnd()
188 var want string
189 if (c >= 0x20 && c <= 0x21) || (c >= 0x23 && c <= 0x5b) || (c >= 0x5d && c <= 0x7e) {
190 want = fmt.Sprintf(`%c`, c)
191 } else {
192 want = fmt.Sprintf(`\u%04x`, c)
193 }
194 wantJSONRecord(t, w, `{"field":"`+want+`"}`)
195 }
196 }
197
View as plain text