1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package trace
16
17 import (
18 "encoding/json"
19 "fmt"
20 "testing"
21
22 "github.com/stretchr/testify/assert"
23 "github.com/stretchr/testify/require"
24 )
25
26
27
28 var testcases = []struct {
29 name string
30 in string
31 tracestate TraceState
32 out string
33 err error
34 }{
35 {
36 name: "duplicate with the same value",
37 in: "foo=1,foo=1",
38 err: errDuplicate,
39 },
40 {
41 name: "duplicate with different values",
42 in: "foo=1,foo=2",
43 err: errDuplicate,
44 },
45 {
46 name: "improperly formatted key/value pair",
47 in: "foo =1",
48 err: errInvalidMember,
49 },
50 {
51 name: "upper case key",
52 in: "FOO=1",
53 err: errInvalidMember,
54 },
55 {
56 name: "key with invalid character",
57 in: "foo.bar=1",
58 err: errInvalidMember,
59 },
60 {
61 name: "multiple keys, one with empty tenant key",
62 in: "foo@=1,bar=2",
63 err: errInvalidMember,
64 },
65 {
66 name: "multiple keys, one with only tenant",
67 in: "@foo=1,bar=2",
68 err: errInvalidMember,
69 },
70 {
71 name: "multiple keys, one with double tenant separator",
72 in: "foo@@bar=1,bar=2",
73 err: errInvalidMember,
74 },
75 {
76 name: "multiple keys, one with multiple tenants",
77 in: "foo@bar@baz=1,bar=2",
78 err: errInvalidMember,
79 },
80 {
81 name: "key too long",
82 in: "foo=1,zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz=1",
83 err: errInvalidMember,
84 },
85 {
86 name: "key too long, with tenant",
87 in: "foo=1,tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt@v=1",
88 err: errInvalidMember,
89 },
90 {
91 name: "tenant too long",
92 in: "foo=1,t@vvvvvvvvvvvvvvv=1",
93 err: errInvalidMember,
94 },
95 {
96 name: "multiple values for a single key",
97 in: "foo=bar=baz",
98 err: errInvalidMember,
99 },
100 {
101 name: "no value",
102 in: "foo=,bar=3",
103 err: errInvalidMember,
104 },
105 {
106 name: "too many members",
107 in: "bar01=01,bar02=02,bar03=03,bar04=04,bar05=05,bar06=06,bar07=07,bar08=08,bar09=09,bar10=10,bar11=11,bar12=12,bar13=13,bar14=14,bar15=15,bar16=16,bar17=17,bar18=18,bar19=19,bar20=20,bar21=21,bar22=22,bar23=23,bar24=24,bar25=25,bar26=26,bar27=27,bar28=28,bar29=29,bar30=30,bar31=31,bar32=32,bar33=33",
108 err: errMemberNumber,
109 },
110 {
111 name: "valid key/value list",
112 in: "abcdefghijklmnopqrstuvwxyz0123456789_-*/= !\"#$%&'()*+-./0123456789:;<>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~",
113 out: "abcdefghijklmnopqrstuvwxyz0123456789_-*/= !\"#$%&'()*+-./0123456789:;<>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~",
114 tracestate: TraceState{list: []member{
115 {
116 Key: "abcdefghijklmnopqrstuvwxyz0123456789_-*/",
117 Value: " !\"#$%&'()*+-./0123456789:;<>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~",
118 },
119 }},
120 },
121 {
122 name: "valid key/value list with tenant",
123 in: "abcdefghijklmnopqrstuvwxyz0123456789_-*/@a-z0-9_-*/= !\"#$%&'()*+-./0123456789:;<>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~",
124 out: "abcdefghijklmnopqrstuvwxyz0123456789_-*/@a-z0-9_-*/= !\"#$%&'()*+-./0123456789:;<>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~",
125 tracestate: TraceState{list: []member{
126 {
127 Key: "abcdefghijklmnopqrstuvwxyz0123456789_-*/@a-z0-9_-*/",
128 Value: " !\"#$%&'()*+-./0123456789:;<>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~",
129 },
130 }},
131 },
132 {
133 name: "empty input",
134
135
136
137 },
138 {
139 name: "single key and value",
140 in: "foo=1",
141 out: "foo=1",
142 tracestate: TraceState{list: []member{
143 {Key: "foo", Value: "1"},
144 }},
145 },
146 {
147 name: "single key and value with empty separator",
148 in: "foo=1,",
149 out: "foo=1",
150 tracestate: TraceState{list: []member{
151 {Key: "foo", Value: "1"},
152 }},
153 },
154 {
155 name: "multiple keys and values",
156 in: "foo=1,bar=2",
157 out: "foo=1,bar=2",
158 tracestate: TraceState{list: []member{
159 {Key: "foo", Value: "1"},
160 {Key: "bar", Value: "2"},
161 }},
162 },
163 {
164 name: "with a key at maximum length",
165 in: "foo=1,zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz=1",
166 out: "foo=1,zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz=1",
167 tracestate: TraceState{list: []member{
168 {
169 Key: "foo",
170 Value: "1",
171 },
172 {
173 Key: "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz",
174 Value: "1",
175 },
176 }},
177 },
178 {
179 name: "with a key and tenant at maximum length",
180 in: "foo=1,ttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt@vvvvvvvvvvvvvv=1",
181 out: "foo=1,ttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt@vvvvvvvvvvvvvv=1",
182 tracestate: TraceState{list: []member{
183 {
184 Key: "foo",
185 Value: "1",
186 },
187 {
188 Key: "ttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt@vvvvvvvvvvvvvv",
189 Value: "1",
190 },
191 }},
192 },
193 {
194 name: "with maximum members",
195 in: "bar01=01,bar02=02,bar03=03,bar04=04,bar05=05,bar06=06,bar07=07,bar08=08,bar09=09,bar10=10,bar11=11,bar12=12,bar13=13,bar14=14,bar15=15,bar16=16,bar17=17,bar18=18,bar19=19,bar20=20,bar21=21,bar22=22,bar23=23,bar24=24,bar25=25,bar26=26,bar27=27,bar28=28,bar29=29,bar30=30,bar31=31,bar32=32",
196 out: "bar01=01,bar02=02,bar03=03,bar04=04,bar05=05,bar06=06,bar07=07,bar08=08,bar09=09,bar10=10,bar11=11,bar12=12,bar13=13,bar14=14,bar15=15,bar16=16,bar17=17,bar18=18,bar19=19,bar20=20,bar21=21,bar22=22,bar23=23,bar24=24,bar25=25,bar26=26,bar27=27,bar28=28,bar29=29,bar30=30,bar31=31,bar32=32",
197 tracestate: TraceState{list: []member{
198 {Key: "bar01", Value: "01"},
199 {Key: "bar02", Value: "02"},
200 {Key: "bar03", Value: "03"},
201 {Key: "bar04", Value: "04"},
202 {Key: "bar05", Value: "05"},
203 {Key: "bar06", Value: "06"},
204 {Key: "bar07", Value: "07"},
205 {Key: "bar08", Value: "08"},
206 {Key: "bar09", Value: "09"},
207 {Key: "bar10", Value: "10"},
208 {Key: "bar11", Value: "11"},
209 {Key: "bar12", Value: "12"},
210 {Key: "bar13", Value: "13"},
211 {Key: "bar14", Value: "14"},
212 {Key: "bar15", Value: "15"},
213 {Key: "bar16", Value: "16"},
214 {Key: "bar17", Value: "17"},
215 {Key: "bar18", Value: "18"},
216 {Key: "bar19", Value: "19"},
217 {Key: "bar20", Value: "20"},
218 {Key: "bar21", Value: "21"},
219 {Key: "bar22", Value: "22"},
220 {Key: "bar23", Value: "23"},
221 {Key: "bar24", Value: "24"},
222 {Key: "bar25", Value: "25"},
223 {Key: "bar26", Value: "26"},
224 {Key: "bar27", Value: "27"},
225 {Key: "bar28", Value: "28"},
226 {Key: "bar29", Value: "29"},
227 {Key: "bar30", Value: "30"},
228 {Key: "bar31", Value: "31"},
229 {Key: "bar32", Value: "32"},
230 }},
231 },
232 {
233 name: "with several members",
234 in: "foo=1,bar=2,rojo=1,congo=2,baz=3",
235 out: "foo=1,bar=2,rojo=1,congo=2,baz=3",
236 tracestate: TraceState{list: []member{
237 {Key: "foo", Value: "1"},
238 {Key: "bar", Value: "2"},
239 {Key: "rojo", Value: "1"},
240 {Key: "congo", Value: "2"},
241 {Key: "baz", Value: "3"},
242 }},
243 },
244 {
245 name: "with tabs between members",
246 in: "foo=1 \t , \t bar=2, \t baz=3",
247 out: "foo=1,bar=2,baz=3",
248 tracestate: TraceState{list: []member{
249 {Key: "foo", Value: "1"},
250 {Key: "bar", Value: "2"},
251 {Key: "baz", Value: "3"},
252 }},
253 },
254 {
255 name: "with multiple tabs between members",
256 in: "foo=1\t \t,\t \tbar=2,\t \tbaz=3",
257 out: "foo=1,bar=2,baz=3",
258 tracestate: TraceState{list: []member{
259 {Key: "foo", Value: "1"},
260 {Key: "bar", Value: "2"},
261 {Key: "baz", Value: "3"},
262 }},
263 },
264 {
265 name: "with space at the end of the member",
266 in: "foo=1 ",
267 out: "foo=1",
268 tracestate: TraceState{list: []member{
269 {Key: "foo", Value: "1"},
270 }},
271 },
272 {
273 name: "with tab at the end of the member",
274 in: "foo=1\t",
275 out: "foo=1",
276 tracestate: TraceState{list: []member{
277 {Key: "foo", Value: "1"},
278 }},
279 },
280 {
281 name: "with tab and space at the end of the member",
282 in: "foo=1 \t",
283 out: "foo=1",
284 tracestate: TraceState{list: []member{
285 {Key: "foo", Value: "1"},
286 }},
287 },
288 }
289
290 var maxMembers = func() TraceState {
291 members := make([]member, maxListMembers)
292 for i := 0; i < maxListMembers; i++ {
293 members[i] = member{
294 Key: fmt.Sprintf("key%d", i+1),
295 Value: fmt.Sprintf("value%d", i+1),
296 }
297 }
298 return TraceState{list: members}
299 }()
300
301 func TestParseTraceState(t *testing.T) {
302 for _, tc := range testcases {
303 t.Run(tc.name, func(t *testing.T) {
304 got, err := ParseTraceState(tc.in)
305 assert.Equal(t, tc.tracestate, got)
306 if tc.err != nil {
307 assert.ErrorIs(t, err, tc.err, tc.in)
308 } else {
309 assert.NoError(t, err, tc.in)
310 }
311 })
312 }
313 }
314
315 func TestTraceStateString(t *testing.T) {
316 for _, tc := range testcases {
317 if tc.err != nil {
318
319 continue
320 }
321 t.Run(tc.name, func(t *testing.T) {
322 assert.Equal(t, tc.out, tc.tracestate.String())
323 })
324 }
325 }
326
327 func TestTraceStateMarshalJSON(t *testing.T) {
328 for _, tc := range testcases {
329 if tc.err != nil {
330
331 continue
332 }
333 t.Run(tc.name, func(t *testing.T) {
334
335 expected, err := json.Marshal(tc.out)
336 require.NoError(t, err)
337
338 actual, err := json.Marshal(tc.tracestate)
339 require.NoError(t, err)
340
341 assert.Equal(t, expected, actual)
342 })
343 }
344 }
345
346 func TestTraceStateGet(t *testing.T) {
347 testCases := []struct {
348 name string
349 key string
350 expected string
351 }{
352 {
353 name: "OK case",
354 key: "key16",
355 expected: "value16",
356 },
357 {
358 name: "not found",
359 key: "keyxx",
360 expected: "",
361 },
362 {
363 name: "invalid W3C key",
364 key: "key!",
365 expected: "",
366 },
367 }
368
369 for _, tc := range testCases {
370 t.Run(tc.name, func(t *testing.T) {
371 assert.Equal(t, tc.expected, maxMembers.Get(tc.key))
372 })
373 }
374 }
375
376 func TestTraceStateDelete(t *testing.T) {
377 ts := TraceState{list: []member{
378 {Key: "key1", Value: "val1"},
379 {Key: "key2", Value: "val2"},
380 {Key: "key3", Value: "val3"},
381 }}
382
383 testCases := []struct {
384 name string
385 key string
386 expected TraceState
387 }{
388 {
389 name: "OK case",
390 key: "key2",
391 expected: TraceState{list: []member{
392 {Key: "key1", Value: "val1"},
393 {Key: "key3", Value: "val3"},
394 }},
395 },
396 {
397 name: "Non-existing key",
398 key: "keyx",
399 expected: TraceState{list: []member{
400 {Key: "key1", Value: "val1"},
401 {Key: "key2", Value: "val2"},
402 {Key: "key3", Value: "val3"},
403 }},
404 },
405 {
406 name: "Invalid key",
407 key: "in va lid",
408 expected: TraceState{list: []member{
409 {Key: "key1", Value: "val1"},
410 {Key: "key2", Value: "val2"},
411 {Key: "key3", Value: "val3"},
412 }},
413 },
414 }
415
416 for _, tc := range testCases {
417 t.Run(tc.name, func(t *testing.T) {
418 assert.Equal(t, tc.expected, ts.Delete(tc.key))
419 })
420 }
421 }
422
423 func TestTraceStateInsert(t *testing.T) {
424 ts := TraceState{list: []member{
425 {Key: "key1", Value: "val1"},
426 {Key: "key2", Value: "val2"},
427 {Key: "key3", Value: "val3"},
428 }}
429
430 testCases := []struct {
431 name string
432 tracestate TraceState
433 key, value string
434 expected TraceState
435 err error
436 }{
437 {
438 name: "add new",
439 tracestate: ts,
440 key: "key4@vendor",
441 value: "val4",
442 expected: TraceState{list: []member{
443 {Key: "key4@vendor", Value: "val4"},
444 {Key: "key1", Value: "val1"},
445 {Key: "key2", Value: "val2"},
446 {Key: "key3", Value: "val3"},
447 }},
448 },
449 {
450 name: "replace",
451 tracestate: ts,
452 key: "key2",
453 value: "valX",
454 expected: TraceState{list: []member{
455 {Key: "key2", Value: "valX"},
456 {Key: "key1", Value: "val1"},
457 {Key: "key3", Value: "val3"},
458 }},
459 },
460 {
461 name: "invalid key",
462 tracestate: ts,
463 key: "key!",
464 value: "val",
465 expected: ts,
466 err: errInvalidKey,
467 },
468 {
469 name: "invalid value",
470 tracestate: ts,
471 key: "key",
472 value: "v=l",
473 expected: ts,
474 err: errInvalidValue,
475 },
476 {
477 name: "invalid key/value",
478 tracestate: ts,
479 key: "key!",
480 value: "v=l",
481 expected: ts,
482 err: errInvalidKey,
483 },
484 {
485 name: "drop the right-most member(oldest) in queue",
486 tracestate: maxMembers,
487 key: "keyx",
488 value: "valx",
489 expected: func() TraceState {
490
491 return TraceState{
492 list: append(
493 []member{{Key: "keyx", Value: "valx"}},
494 maxMembers.list[:len(maxMembers.list)-1]...,
495 ),
496 }
497 }(),
498 },
499 }
500
501 for _, tc := range testCases {
502 t.Run(tc.name, func(t *testing.T) {
503 actual, err := tc.tracestate.Insert(tc.key, tc.value)
504 assert.ErrorIs(t, err, tc.err, tc.name)
505 if tc.err != nil {
506 assert.Equal(t, tc.tracestate, actual)
507 } else {
508 assert.Equal(t, tc.expected, actual)
509 }
510 })
511 }
512 }
513
514 func TestTraceStateLen(t *testing.T) {
515 ts := TraceState{}
516 assert.Equal(t, 0, ts.Len(), "zero value TraceState is empty")
517
518 key := "key"
519 ts = TraceState{list: []member{{key, "value"}}}
520 assert.Equal(t, 1, ts.Len(), "TraceState with one value")
521 }
522
523 func TestTraceStateImmutable(t *testing.T) {
524 k0, v0 := "k0", "v0"
525 ts0 := TraceState{list: []member{{k0, v0}}}
526 assert.Equal(t, v0, ts0.Get(k0))
527
528
529 k1, v1 := "k1", "v1"
530 ts1, err := ts0.Insert(k1, v1)
531 require.NoError(t, err)
532 assert.Equal(t, v0, ts0.Get(k0))
533 assert.Equal(t, "", ts0.Get(k1))
534 assert.Equal(t, v0, ts1.Get(k0))
535 assert.Equal(t, v1, ts1.Get(k1))
536
537
538 v2 := "v2"
539 ts2, err := ts1.Insert(k1, v2)
540 require.NoError(t, err)
541 assert.Equal(t, v0, ts0.Get(k0))
542 assert.Equal(t, "", ts0.Get(k1))
543 assert.Equal(t, v0, ts1.Get(k0))
544 assert.Equal(t, v1, ts1.Get(k1))
545 assert.Equal(t, v0, ts2.Get(k0))
546 assert.Equal(t, v2, ts2.Get(k1))
547
548
549 ts3 := ts2.Delete(k0)
550 assert.Equal(t, v0, ts0.Get(k0))
551 assert.Equal(t, v0, ts1.Get(k0))
552 assert.Equal(t, v0, ts2.Get(k0))
553 assert.Equal(t, "", ts3.Get(k0))
554 }
555
556 func BenchmarkParseTraceState(b *testing.B) {
557 benches := []struct {
558 name string
559 in string
560 }{
561 {
562 name: "single key",
563 in: "somewhatRealisticKeyLength=someValueAbcdefgh1234567890",
564 },
565 {
566 name: "tenant single key",
567 in: "somewhatRealisticKeyLength@someTenant=someValueAbcdefgh1234567890",
568 },
569 {
570 name: "three keys",
571 in: "someKeyName.One=someValue1,someKeyName.Two=someValue2,someKeyName.Three=someValue3",
572 },
573 {
574 name: "tenant three keys",
575 in: "someKeyName.One@tenant=someValue1,someKeyName.Two@tenant=someValue2,someKeyName.Three@tenant=someValue3",
576 },
577 }
578 for _, bench := range benches {
579 b.Run(bench.name, func(b *testing.B) {
580 b.ReportAllocs()
581 b.ResetTimer()
582
583 for i := 0; i < b.N; i++ {
584 _, _ = ParseTraceState(bench.in)
585 }
586 })
587 }
588 }
589
View as plain text