1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2026-06-03 18:35:08 +02:00
Files
opentelemetry-go/trace/trace_test.go
T

574 lines
14 KiB
Go
Raw Normal View History

// Copyright The OpenTelemetry Authors
2024-02-29 07:05:28 +01:00
// SPDX-License-Identifier: Apache-2.0
2019-07-19 15:29:42 -05:00
2020-11-06 23:13:31 +01:00
package trace
2019-07-19 15:29:42 -05:00
import (
"bytes"
2019-07-19 15:29:42 -05:00
"testing"
2020-10-22 05:30:28 +08:00
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"
2019-07-19 15:29:42 -05:00
)
func TestSpanContextIsValid(t *testing.T) {
2019-08-06 00:58:51 +08:00
for _, testcase := range []struct {
name string
2020-10-08 19:58:56 -07:00
tid TraceID
sid SpanID
2019-08-06 00:58:51 +08:00
want bool
}{
{
name: "SpanContext.IsValid() returns true if sc has both an Trace ID and Span ID",
tid: [16]byte{1},
sid: [8]byte{42},
2019-08-06 00:58:51 +08:00
want: true,
}, {
name: "SpanContext.IsValid() returns false if sc has neither an Trace ID nor Span ID",
2020-10-08 19:58:56 -07:00
tid: TraceID([16]byte{}),
sid: [8]byte{},
2019-08-06 00:58:51 +08:00
want: false,
}, {
name: "SpanContext.IsValid() returns false if sc has a Span ID but not a Trace ID",
2020-10-08 19:58:56 -07:00
tid: TraceID([16]byte{}),
sid: [8]byte{42},
2019-08-06 00:58:51 +08:00
want: false,
}, {
name: "SpanContext.IsValid() returns false if sc has a Trace ID but not a Span ID",
2020-10-08 19:58:56 -07:00
tid: TraceID([16]byte{1}),
sid: [8]byte{},
want: false,
2019-08-06 00:58:51 +08:00
},
} {
t.Run(testcase.name, func(t *testing.T) {
2020-10-08 19:58:56 -07:00
sc := SpanContext{
2021-03-09 11:17:29 -05:00
traceID: testcase.tid,
spanID: testcase.sid,
2019-08-06 00:58:51 +08:00
}
have := sc.IsValid()
if have != testcase.want {
t.Errorf("Want: %v, but have: %v", testcase.want, have)
}
})
}
}
func TestSpanContextEqual(t *testing.T) {
a := SpanContext{
traceID: [16]byte{1},
spanID: [8]byte{42},
}
b := SpanContext{
traceID: [16]byte{1},
spanID: [8]byte{42},
}
c := SpanContext{
traceID: [16]byte{2},
spanID: [8]byte{42},
}
if !a.Equal(b) {
t.Error("Want: true, but have: false")
}
if a.Equal(c) {
t.Error("Want: false, but have: true")
}
}
func TestSpanContextIsSampled(t *testing.T) {
for _, testcase := range []struct {
name string
tf TraceFlags
want bool
}{
{
name: "SpanContext.IsSampled() returns false if sc is not sampled",
want: false,
}, {
name: "SpanContext.IsSampled() returns true if sc is sampled",
tf: FlagsSampled,
want: true,
},
} {
t.Run(testcase.name, func(t *testing.T) {
sc := SpanContext{
traceFlags: testcase.tf,
}
have := sc.IsSampled()
if have != testcase.want {
t.Errorf("Want: %v, but have: %v", testcase.want, have)
}
})
}
}
func TestSpanContextIsRemote(t *testing.T) {
for _, testcase := range []struct {
name string
remote bool
want bool
}{
{
name: "SpanContext.IsRemote() returns false if sc is not remote",
want: false,
}, {
name: "SpanContext.IsRemote() returns true if sc is remote",
remote: true,
want: true,
},
} {
t.Run(testcase.name, func(t *testing.T) {
sc := SpanContext{
remote: testcase.remote,
}
have := sc.IsRemote()
if have != testcase.want {
t.Errorf("Want: %v, but have: %v", testcase.want, have)
}
})
}
}
func TestSpanContextMarshalJSON(t *testing.T) {
for _, testcase := range []struct {
name string
tid TraceID
sid SpanID
tstate TraceState
tflags TraceFlags
isRemote bool
want []byte
}{
{
name: "SpanContext.MarshalJSON() returns json with partial data",
tid: [16]byte{1},
sid: [8]byte{42},
want: []byte(`{"TraceID":"01000000000000000000000000000000","SpanID":"2a00000000000000","TraceFlags":"00","TraceState":"","Remote":false}`),
},
{
name: "SpanContext.MarshalJSON() returns json with full data",
tid: [16]byte{1},
sid: [8]byte{42},
tflags: FlagsSampled,
isRemote: true,
tstate: TraceState{list: []member{
{Key: "foo", Value: "1"},
}},
want: []byte(`{"TraceID":"01000000000000000000000000000000","SpanID":"2a00000000000000","TraceFlags":"01","TraceState":"foo=1","Remote":true}`),
},
} {
t.Run(testcase.name, func(t *testing.T) {
sc := SpanContext{
traceID: testcase.tid,
spanID: testcase.sid,
traceFlags: testcase.tflags,
traceState: testcase.tstate,
remote: testcase.isRemote,
}
have, err := sc.MarshalJSON()
if err != nil {
t.Errorf("Marshaling failed: %v", err)
}
if !bytes.Equal(have, testcase.want) {
t.Errorf("Want: %v, but have: %v", string(testcase.want), string(have))
}
})
}
}
func TestSpanIDFromHex(t *testing.T) {
for _, testcase := range []struct {
name string
hex string
sid SpanID
valid bool
}{
{
name: "Valid SpanID",
sid: SpanID([8]byte{42}),
hex: "2a00000000000000",
valid: true,
}, {
name: "Invalid SpanID with invalid length",
hex: "80f198ee56343ba",
valid: false,
}, {
name: "Invalid SpanID with invalid char",
hex: "80f198ee563433g7",
valid: false,
}, {
name: "Invalid SpanID with uppercase",
hex: "80f198ee53ba86F7",
valid: false,
}, {
name: "Invalid SpanID with zero value",
hex: "0000000000000000",
valid: false,
},
} {
t.Run(testcase.name, func(t *testing.T) {
sid, err := SpanIDFromHex(testcase.hex)
if testcase.valid && err != nil {
t.Errorf("Expected SpanID %s to be valid but end with error %s", testcase.hex, err.Error())
} else if !testcase.valid && err == nil {
t.Errorf("Expected SpanID %s to be invalid but end no error", testcase.hex)
}
if sid != testcase.sid {
t.Errorf("Want: %v, but have: %v", testcase.sid, sid)
}
})
}
}
func TestIsValidFromHex(t *testing.T) {
for _, testcase := range []struct {
name string
hex string
2020-10-08 19:58:56 -07:00
tid TraceID
valid bool
}{
{
name: "Valid TraceID",
2020-10-08 19:58:56 -07:00
tid: TraceID([16]byte{128, 241, 152, 238, 86, 52, 59, 168, 100, 254, 139, 42, 87, 211, 239, 247}),
hex: "80f198ee56343ba864fe8b2a57d3eff7",
valid: true,
}, {
name: "Invalid TraceID with invalid length",
hex: "80f198ee56343ba864fe8b2a57d3eff",
valid: false,
}, {
name: "Invalid TraceID with invalid char",
hex: "80f198ee56343ba864fe8b2a57d3efg7",
valid: false,
}, {
name: "Invalid TraceID with uppercase",
hex: "80f198ee56343ba864fe8b2a57d3efF7",
valid: false,
}, {
name: "Invalid TraceID with zero value",
hex: "00000000000000000000000000000000",
valid: false,
},
} {
t.Run(testcase.name, func(t *testing.T) {
2020-10-08 19:58:56 -07:00
tid, err := TraceIDFromHex(testcase.hex)
if testcase.valid && err != nil {
t.Errorf("Expected TraceID %s to be valid but end with error %s", testcase.hex, err.Error())
}
if !testcase.valid && err == nil {
t.Errorf("Expected TraceID %s to be invalid but end no error", testcase.hex)
}
if tid != testcase.tid {
t.Errorf("Want: %v, but have: %v", testcase.tid, tid)
}
})
}
}
func TestSpanContextHasTraceID(t *testing.T) {
2019-07-19 15:29:42 -05:00
for _, testcase := range []struct {
name string
2020-10-08 19:58:56 -07:00
tid TraceID
2019-07-19 15:29:42 -05:00
want bool
}{
{
name: "SpanContext.HasTraceID() returns true if both Low and High are nonzero",
2020-10-08 19:58:56 -07:00
tid: TraceID([16]byte{1}),
2019-07-19 15:29:42 -05:00
want: true,
}, {
name: "SpanContext.HasTraceID() returns false if neither Low nor High are nonzero",
2020-10-08 19:58:56 -07:00
tid: TraceID{},
2019-07-19 15:29:42 -05:00
want: false,
},
} {
t.Run(testcase.name, func(t *testing.T) {
2023-10-16 10:02:21 -07:00
// proto: func (sc SpanContext) HasTraceID() bool{}
2021-03-09 11:17:29 -05:00
sc := SpanContext{traceID: testcase.tid}
2019-07-19 15:29:42 -05:00
have := sc.HasTraceID()
if have != testcase.want {
t.Errorf("Want: %v, but have: %v", testcase.want, have)
}
})
}
}
func TestSpanContextHasSpanID(t *testing.T) {
2019-07-19 15:29:42 -05:00
for _, testcase := range []struct {
name string
2020-10-08 19:58:56 -07:00
sc SpanContext
2019-07-19 15:29:42 -05:00
want bool
}{
{
name: "SpanContext.HasSpanID() returns true if self.SpanID != 0",
2021-03-09 11:17:29 -05:00
sc: SpanContext{spanID: [8]byte{42}},
2019-07-19 15:29:42 -05:00
want: true,
}, {
name: "SpanContext.HasSpanID() returns false if self.SpanID == 0",
2020-10-08 19:58:56 -07:00
sc: SpanContext{},
2019-07-19 15:29:42 -05:00
want: false,
},
} {
t.Run(testcase.name, func(t *testing.T) {
2023-10-16 10:02:21 -07:00
// proto: func (sc SpanContext) HasSpanID() bool {}
2019-07-19 15:29:42 -05:00
have := testcase.sc.HasSpanID()
if have != testcase.want {
t.Errorf("Want: %v, but have: %v", testcase.want, have)
}
})
}
}
2021-04-05 13:21:42 -04:00
func TestTraceFlagsIsSampled(t *testing.T) {
2019-07-19 15:29:42 -05:00
for _, testcase := range []struct {
name string
2021-04-05 13:21:42 -04:00
tf TraceFlags
want bool
2019-07-19 15:29:42 -05:00
}{
{
name: "sampled",
2021-04-05 13:21:42 -04:00
tf: FlagsSampled,
want: true,
2019-07-19 15:29:42 -05:00
}, {
2020-07-07 16:38:52 -07:00
name: "unused bits are ignored, still not sampled",
2021-04-05 13:21:42 -04:00
tf: ^FlagsSampled,
2020-07-07 16:38:52 -07:00
want: false,
}, {
name: "unused bits are ignored, still sampled",
2021-04-05 13:21:42 -04:00
tf: FlagsSampled | ^FlagsSampled,
want: true,
}, {
name: "not sampled/default",
want: false,
2019-07-19 15:29:42 -05:00
},
} {
t.Run(testcase.name, func(t *testing.T) {
2021-04-05 13:21:42 -04:00
have := testcase.tf.IsSampled()
if have != testcase.want {
t.Errorf("Want: %v, but have: %v", testcase.want, have)
}
})
}
}
func TestTraceFlagsWithSampled(t *testing.T) {
for _, testcase := range []struct {
name string
start TraceFlags
sample bool
want TraceFlags
}{
{
name: "sampled unchanged",
start: FlagsSampled,
want: FlagsSampled,
sample: true,
}, {
name: "become sampled",
want: FlagsSampled,
sample: true,
}, {
name: "unused bits are ignored, still not sampled",
start: ^FlagsSampled,
want: ^FlagsSampled,
sample: false,
}, {
name: "unused bits are ignored, becomes sampled",
start: ^FlagsSampled,
want: FlagsSampled | ^FlagsSampled,
sample: true,
}, {
name: "not sampled/default",
sample: false,
},
} {
t.Run(testcase.name, func(t *testing.T) {
have := testcase.start.WithSampled(testcase.sample)
2019-07-19 15:29:42 -05:00
if have != testcase.want {
t.Errorf("Want: %v, but have: %v", testcase.want, have)
2019-07-19 15:29:42 -05:00
}
})
}
}
func TestStringTraceID(t *testing.T) {
2019-07-19 15:29:42 -05:00
for _, testcase := range []struct {
name string
2020-10-08 19:58:56 -07:00
tid TraceID
2019-07-19 15:29:42 -05:00
want string
}{
{
name: "TraceID.String returns string representation of self.TraceID values > 0",
2020-10-08 19:58:56 -07:00
tid: TraceID([16]byte{255}),
want: "ff000000000000000000000000000000",
},
{
name: "TraceID.String returns string representation of self.TraceID values == 0",
2020-10-08 19:58:56 -07:00
tid: TraceID([16]byte{}),
want: "00000000000000000000000000000000",
2019-07-19 15:29:42 -05:00
},
} {
t.Run(testcase.name, func(t *testing.T) {
2023-10-16 10:02:21 -07:00
// proto: func (t TraceID) String() string {}
have := testcase.tid.String()
2019-07-19 15:29:42 -05:00
if have != testcase.want {
t.Errorf("Want: %s, but have: %s", testcase.want, have)
}
})
}
}
func TestStringSpanID(t *testing.T) {
for _, testcase := range []struct {
name string
2020-10-08 19:58:56 -07:00
sid SpanID
want string
}{
{
name: "SpanID.String returns string representation of self.SpanID values > 0",
2020-10-08 19:58:56 -07:00
sid: SpanID([8]byte{255}),
want: "ff00000000000000",
},
{
name: "SpanID.String returns string representation of self.SpanID values == 0",
2020-10-08 19:58:56 -07:00
sid: SpanID([8]byte{}),
want: "0000000000000000",
},
} {
t.Run(testcase.name, func(t *testing.T) {
2023-10-16 10:02:21 -07:00
// proto: func (t TraceID) String() string {}
have := testcase.sid.String()
if have != testcase.want {
t.Errorf("Want: %s, but have: %s", testcase.want, have)
}
})
}
}
2020-10-08 19:58:56 -07:00
func assertSpanContextEqual(got, want SpanContext) bool {
2021-03-09 11:17:29 -05:00
return got.spanID == want.spanID &&
got.traceID == want.traceID &&
got.traceFlags == want.traceFlags &&
got.remote == want.remote &&
2021-05-24 14:53:26 +00:00
got.traceState.String() == want.traceState.String()
}
2021-03-09 11:17:29 -05:00
func TestNewSpanContext(t *testing.T) {
testCases := []struct {
name string
config SpanContextConfig
expectedSpanContext SpanContext
}{
{
name: "Complete SpanContext",
config: SpanContextConfig{
TraceID: TraceID([16]byte{1}),
SpanID: SpanID([8]byte{42}),
TraceFlags: 0x1,
2021-05-24 14:53:26 +00:00
TraceState: TraceState{list: []member{
{"foo", "bar"},
2021-03-09 11:17:29 -05:00
}},
},
expectedSpanContext: SpanContext{
traceID: TraceID([16]byte{1}),
spanID: SpanID([8]byte{42}),
traceFlags: 0x1,
2021-05-24 14:53:26 +00:00
traceState: TraceState{list: []member{
{"foo", "bar"},
2021-03-09 11:17:29 -05:00
}},
},
},
{
name: "Empty SpanContext",
config: SpanContextConfig{},
expectedSpanContext: SpanContext{},
},
{
name: "Partial SpanContext",
config: SpanContextConfig{
TraceID: TraceID([16]byte{1}),
SpanID: SpanID([8]byte{42}),
},
expectedSpanContext: SpanContext{
traceID: TraceID([16]byte{1}),
spanID: SpanID([8]byte{42}),
traceFlags: 0x0,
traceState: TraceState{},
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
sctx := NewSpanContext(tc.config)
if !assertSpanContextEqual(sctx, tc.expectedSpanContext) {
t.Fatalf("%s: Unexpected context created: %s", tc.name, cmp.Diff(sctx, tc.expectedSpanContext))
}
})
}
}
func TestSpanContextDerivation(t *testing.T) {
from := SpanContext{}
to := SpanContext{traceID: TraceID([16]byte{1})}
modified := from.WithTraceID(to.TraceID())
if !assertSpanContextEqual(modified, to) {
t.Fatalf("WithTraceID: Unexpected context created: %s", cmp.Diff(modified, to))
}
from = to
to.spanID = SpanID([8]byte{42})
modified = from.WithSpanID(to.SpanID())
if !assertSpanContextEqual(modified, to) {
t.Fatalf("WithSpanID: Unexpected context created: %s", cmp.Diff(modified, to))
}
from = to
to.traceFlags = 0x13
modified = from.WithTraceFlags(to.TraceFlags())
if !assertSpanContextEqual(modified, to) {
t.Fatalf("WithTraceFlags: Unexpected context created: %s", cmp.Diff(modified, to))
}
from = to
2021-05-24 14:53:26 +00:00
to.traceState = TraceState{list: []member{{"foo", "bar"}}}
2021-03-09 11:17:29 -05:00
modified = from.WithTraceState(to.TraceState())
if !assertSpanContextEqual(modified, to) {
t.Fatalf("WithTraceState: Unexpected context created: %s", cmp.Diff(modified, to))
}
}
2024-07-11 08:14:57 -07:00
func TestConfigLinkMutability(t *testing.T) {
sc0 := NewSpanContext(SpanContextConfig{TraceID: [16]byte{1}})
sc1 := NewSpanContext(SpanContextConfig{TraceID: [16]byte{2}})
sc2 := NewSpanContext(SpanContextConfig{TraceID: [16]byte{3}})
l0 := Link{SpanContext: sc0}
l1 := Link{SpanContext: sc1}
l2 := Link{SpanContext: sc2}
links := []Link{l0, l1}
conf := NewSpanStartConfig(WithLinks(links...))
// Mutating passed arg should not change configured links.
links[0] = l2
want := SpanConfig{links: []Link{l0, l1}}
assert.Equal(t, want, conf)
}