1// Copyright (c) 2017 Uber Technologies, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package jaeger
16
17import (
18	"math"
19	"testing"
20
21	"github.com/stretchr/testify/assert"
22	"github.com/stretchr/testify/require"
23)
24
25func TestContextFromString(t *testing.T) {
26	var err error
27	_, err = ContextFromString("")
28	assert.Error(t, err)
29	_, err = ContextFromString("abcd")
30	assert.Error(t, err)
31	_, err = ContextFromString("x:1:1:1")
32	assert.Error(t, err)
33	_, err = ContextFromString("1:x:1:1")
34	assert.Error(t, err)
35	_, err = ContextFromString("1:1:x:1")
36	assert.Error(t, err)
37	_, err = ContextFromString("1:1:1:x")
38	assert.Error(t, err)
39	_, err = ContextFromString("1:1:1:x")
40	assert.Error(t, err)
41	_, err = ContextFromString("01234567890123456789012345678901234:1:1:1")
42	assert.Error(t, err)
43	_, err = ContextFromString("01234567890123456789012345678901:1:1:1")
44	assert.NoError(t, err)
45	_, err = ContextFromString("01234_67890123456789012345678901:1:1:1")
46	assert.Error(t, err)
47	_, err = ContextFromString("0123456789012345678901_345678901:1:1:1")
48	assert.Error(t, err)
49	_, err = ContextFromString("1:0123456789012345:1:1")
50	assert.NoError(t, err)
51	_, err = ContextFromString("1:01234567890123456:1:1")
52	assert.Error(t, err)
53	ctx, err := ContextFromString("10000000000000001:1:1:1")
54	assert.NoError(t, err)
55	assert.EqualValues(t, TraceID{High: 1, Low: 1}, ctx.traceID)
56	ctx, err = ContextFromString("1:1:1:1")
57	assert.NoError(t, err)
58	assert.EqualValues(t, TraceID{Low: 1}, ctx.traceID)
59	assert.EqualValues(t, 1, ctx.spanID)
60	assert.EqualValues(t, 1, ctx.parentID)
61	assert.True(t, ctx.IsSampled())
62	ctx = NewSpanContext(TraceID{Low: 1}, 1, 1, true, nil)
63	assert.EqualValues(t, TraceID{Low: 1}, ctx.traceID)
64	assert.EqualValues(t, 1, ctx.spanID)
65	assert.EqualValues(t, 1, ctx.parentID)
66	assert.True(t, ctx.IsSampled())
67	assert.Equal(t, "00000000000000ff", SpanID(255).String())
68	assert.Equal(t, "00000000000000ff", TraceID{Low: 255}.String())
69	assert.Equal(t, "00000000000000ff00000000000000ff", TraceID{High: 255, Low: 255}.String())
70	ctx = NewSpanContext(TraceID{High: 255, Low: 255}, SpanID(1), SpanID(1), false, nil)
71	assert.Equal(t, "00000000000000ff00000000000000ff:0000000000000001:0000000000000001:0", ctx.String())
72}
73
74func TestSpanContext_WithBaggageItem(t *testing.T) {
75	var ctx SpanContext
76	ctx = ctx.WithBaggageItem("some-KEY", "Some-Value")
77	assert.Equal(t, map[string]string{"some-KEY": "Some-Value"}, ctx.baggage)
78	ctx = ctx.WithBaggageItem("some-KEY", "Some-Other-Value")
79	assert.Equal(t, map[string]string{"some-KEY": "Some-Other-Value"}, ctx.baggage)
80}
81
82func TestSpanContext_WithBaggageItem_Delete(t *testing.T) {
83	var ctx SpanContext
84	ctx = ctx.WithBaggageItem("some-KEY", "")
85	assert.Nil(t, ctx.baggage)
86	ctx = ctx.WithBaggageItem("some-KEY", "Some-Value")
87	assert.Equal(t, map[string]string{"some-KEY": "Some-Value"}, ctx.baggage)
88	ctx = ctx.WithBaggageItem("another-KEY", "")
89	assert.Equal(t, map[string]string{"some-KEY": "Some-Value"}, ctx.baggage)
90	ctx2 := ctx.WithBaggageItem("some-KEY", "")
91	assert.Equal(t, map[string]string{"some-KEY": "Some-Value"}, ctx.baggage, "parent unchanged")
92	assert.Equal(t, map[string]string{}, ctx2.baggage)
93}
94
95func TestSpanContext_Flags(t *testing.T) {
96
97	var tests = map[string]struct {
98		in           string
99		sampledFlag  bool
100		debugFlag    bool
101		firehoseFlag bool
102	}{
103		"None": {
104			in:           "1:1:1:0",
105			sampledFlag:  false,
106			debugFlag:    false,
107			firehoseFlag: false,
108		},
109		"Sampled Only": {
110			in:           "1:1:1:1",
111			sampledFlag:  true,
112			debugFlag:    false,
113			firehoseFlag: false,
114		},
115
116		"Debug Only": {
117			in:           "1:1:1:2",
118			sampledFlag:  false,
119			debugFlag:    true,
120			firehoseFlag: false,
121		},
122
123		"IsFirehose Only": {
124			in:           "1:1:1:8",
125			sampledFlag:  false,
126			debugFlag:    false,
127			firehoseFlag: true,
128		},
129
130		"Sampled And Debug": {
131			in:           "1:1:1:3",
132			sampledFlag:  true,
133			debugFlag:    true,
134			firehoseFlag: false,
135		},
136
137		"Sampled And Firehose": {
138			in:           "1:1:1:9",
139			sampledFlag:  true,
140			debugFlag:    false,
141			firehoseFlag: true,
142		},
143
144		"Debug And Firehose": {
145			in:           "1:1:1:10",
146			sampledFlag:  false,
147			debugFlag:    true,
148			firehoseFlag: true,
149		},
150
151		"Sampled And Debug And Firehose": {
152			in:           "1:1:1:11",
153			sampledFlag:  true,
154			debugFlag:    true,
155			firehoseFlag: true,
156		},
157	}
158
159	for name, tc := range tests {
160		t.Run(name, func(t *testing.T) {
161			ctx, err := ContextFromString(tc.in)
162			require.NoError(t, err)
163			assert.Equal(t, tc.sampledFlag, ctx.IsSampled())
164			assert.Equal(t, tc.debugFlag, ctx.IsDebug())
165			assert.Equal(t, tc.firehoseFlag, ctx.IsFirehose())
166		})
167	}
168}
169
170func TestSpanContext_CopyFrom(t *testing.T) {
171	ctx, err := ContextFromString("1:1:1:1")
172	require.NoError(t, err)
173	ctx2 := SpanContext{}
174	ctx2.CopyFrom(&ctx)
175	assert.Equal(t, ctx, ctx2)
176	// with baggage
177	ctx = ctx.WithBaggageItem("x", "y")
178	ctx2 = SpanContext{}
179	ctx2.CopyFrom(&ctx)
180	assert.Equal(t, ctx, ctx2)
181	assert.Equal(t, "y", ctx2.baggage["x"])
182}
183
184func TestTraceIDString(t *testing.T) {
185	var tests = map[string]struct {
186		in       TraceID
187		expected string
188	}{
189		"Empty TraceID": {
190			in:       TraceID{},
191			expected: "0000000000000000",
192		},
193		"TraceID low only": {
194			in:       TraceID{Low: math.MaxUint64/16 - 405},
195			expected: "0ffffffffffffe6a",
196		},
197		"TraceID low and high": {
198			in:       TraceID{High: math.MaxUint64 / 16, Low: math.MaxUint64/16 - 405},
199			expected: "0fffffffffffffff0ffffffffffffe6a",
200		},
201	}
202	for name, tc := range tests {
203		t.Run(name, func(t *testing.T) {
204			assert.Equal(t, tc.expected, tc.in.String())
205			parsed, err := TraceIDFromString(tc.in.String())
206			assert.NoError(t, err)
207			assert.Equal(t, tc.in, parsed)
208		})
209	}
210}
211
212func TestSpanIDString(t *testing.T) {
213	var tests = map[string]struct {
214		in       SpanID
215		expected string
216	}{
217		"SpanID zero": {
218			in:       0,
219			expected: "0000000000000000",
220		},
221		"SpanID non zero": {
222			in:       math.MaxUint64/16 - 405,
223			expected: "0ffffffffffffe6a",
224		},
225	}
226	for name, tc := range tests {
227		t.Run(name, func(t *testing.T) {
228			assert.Equal(t, tc.expected, tc.in.String())
229			parsed, err := SpanIDFromString(tc.in.String())
230			assert.NoError(t, err)
231			assert.Equal(t, tc.in, parsed)
232		})
233	}
234}
235