| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662 |
- // Copyright The OpenTelemetry Authors
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package trace
- import (
- "bytes"
- "context"
- "testing"
- "github.com/google/go-cmp/cmp"
- "github.com/stretchr/testify/assert"
- "go.opentelemetry.io/otel/attribute"
- )
- func TestSpanContextIsValid(t *testing.T) {
- for _, testcase := range []struct {
- name string
- tid TraceID
- sid SpanID
- 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},
- want: true,
- }, {
- name: "SpanContext.IsValid() returns false if sc has neither an Trace ID nor Span ID",
- tid: TraceID([16]byte{}),
- sid: [8]byte{},
- want: false,
- }, {
- name: "SpanContext.IsValid() returns false if sc has a Span ID but not a Trace ID",
- tid: TraceID([16]byte{}),
- sid: [8]byte{42},
- want: false,
- }, {
- name: "SpanContext.IsValid() returns false if sc has a Trace ID but not a Span ID",
- tid: TraceID([16]byte{1}),
- sid: [8]byte{},
- want: false,
- },
- } {
- t.Run(testcase.name, func(t *testing.T) {
- sc := SpanContext{
- traceID: testcase.tid,
- spanID: testcase.sid,
- }
- 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
- tid TraceID
- valid bool
- }{
- {
- name: "Valid TraceID",
- 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) {
- 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) {
- for _, testcase := range []struct {
- name string
- tid TraceID
- want bool
- }{
- {
- name: "SpanContext.HasTraceID() returns true if both Low and High are nonzero",
- tid: TraceID([16]byte{1}),
- want: true,
- }, {
- name: "SpanContext.HasTraceID() returns false if neither Low nor High are nonzero",
- tid: TraceID{},
- want: false,
- },
- } {
- t.Run(testcase.name, func(t *testing.T) {
- //proto: func (sc SpanContext) HasTraceID() bool{}
- sc := SpanContext{traceID: testcase.tid}
- have := sc.HasTraceID()
- if have != testcase.want {
- t.Errorf("Want: %v, but have: %v", testcase.want, have)
- }
- })
- }
- }
- func TestSpanContextHasSpanID(t *testing.T) {
- for _, testcase := range []struct {
- name string
- sc SpanContext
- want bool
- }{
- {
- name: "SpanContext.HasSpanID() returns true if self.SpanID != 0",
- sc: SpanContext{spanID: [8]byte{42}},
- want: true,
- }, {
- name: "SpanContext.HasSpanID() returns false if self.SpanID == 0",
- sc: SpanContext{},
- want: false,
- },
- } {
- t.Run(testcase.name, func(t *testing.T) {
- //proto: func (sc SpanContext) HasSpanID() bool {}
- have := testcase.sc.HasSpanID()
- if have != testcase.want {
- t.Errorf("Want: %v, but have: %v", testcase.want, have)
- }
- })
- }
- }
- func TestTraceFlagsIsSampled(t *testing.T) {
- for _, testcase := range []struct {
- name string
- tf TraceFlags
- want bool
- }{
- {
- name: "sampled",
- tf: FlagsSampled,
- want: true,
- }, {
- name: "unused bits are ignored, still not sampled",
- tf: ^FlagsSampled,
- want: false,
- }, {
- name: "unused bits are ignored, still sampled",
- tf: FlagsSampled | ^FlagsSampled,
- want: true,
- }, {
- name: "not sampled/default",
- want: false,
- },
- } {
- t.Run(testcase.name, func(t *testing.T) {
- 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)
- if have != testcase.want {
- t.Errorf("Want: %v, but have: %v", testcase.want, have)
- }
- })
- }
- }
- func TestStringTraceID(t *testing.T) {
- for _, testcase := range []struct {
- name string
- tid TraceID
- want string
- }{
- {
- name: "TraceID.String returns string representation of self.TraceID values > 0",
- tid: TraceID([16]byte{255}),
- want: "ff000000000000000000000000000000",
- },
- {
- name: "TraceID.String returns string representation of self.TraceID values == 0",
- tid: TraceID([16]byte{}),
- want: "00000000000000000000000000000000",
- },
- } {
- t.Run(testcase.name, func(t *testing.T) {
- //proto: func (t TraceID) String() string {}
- have := testcase.tid.String()
- 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
- sid SpanID
- want string
- }{
- {
- name: "SpanID.String returns string representation of self.SpanID values > 0",
- sid: SpanID([8]byte{255}),
- want: "ff00000000000000",
- },
- {
- name: "SpanID.String returns string representation of self.SpanID values == 0",
- sid: SpanID([8]byte{}),
- want: "0000000000000000",
- },
- } {
- t.Run(testcase.name, func(t *testing.T) {
- //proto: func (t TraceID) String() string {}
- have := testcase.sid.String()
- if have != testcase.want {
- t.Errorf("Want: %s, but have: %s", testcase.want, have)
- }
- })
- }
- }
- func TestValidateSpanKind(t *testing.T) {
- tests := []struct {
- in SpanKind
- want SpanKind
- }{
- {
- SpanKindUnspecified,
- SpanKindInternal,
- },
- {
- SpanKindInternal,
- SpanKindInternal,
- },
- {
- SpanKindServer,
- SpanKindServer,
- },
- {
- SpanKindClient,
- SpanKindClient,
- },
- {
- SpanKindProducer,
- SpanKindProducer,
- },
- {
- SpanKindConsumer,
- SpanKindConsumer,
- },
- }
- for _, test := range tests {
- if got := ValidateSpanKind(test.in); got != test.want {
- t.Errorf("ValidateSpanKind(%#v) = %#v, want %#v", test.in, got, test.want)
- }
- }
- }
- func TestSpanKindString(t *testing.T) {
- tests := []struct {
- in SpanKind
- want string
- }{
- {
- SpanKindUnspecified,
- "unspecified",
- },
- {
- SpanKindInternal,
- "internal",
- },
- {
- SpanKindServer,
- "server",
- },
- {
- SpanKindClient,
- "client",
- },
- {
- SpanKindProducer,
- "producer",
- },
- {
- SpanKindConsumer,
- "consumer",
- },
- }
- for _, test := range tests {
- if got := test.in.String(); got != test.want {
- t.Errorf("%#v.String() = %#v, want %#v", test.in, got, test.want)
- }
- }
- }
- func assertSpanContextEqual(got SpanContext, want SpanContext) bool {
- return got.spanID == want.spanID &&
- got.traceID == want.traceID &&
- got.traceFlags == want.traceFlags &&
- got.remote == want.remote &&
- got.traceState.String() == want.traceState.String()
- }
- 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,
- TraceState: TraceState{list: []member{
- {"foo", "bar"},
- }},
- },
- expectedSpanContext: SpanContext{
- traceID: TraceID([16]byte{1}),
- spanID: SpanID([8]byte{42}),
- traceFlags: 0x1,
- traceState: TraceState{list: []member{
- {"foo", "bar"},
- }},
- },
- },
- {
- 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
- to.traceState = TraceState{list: []member{{"foo", "bar"}}}
- modified = from.WithTraceState(to.TraceState())
- if !assertSpanContextEqual(modified, to) {
- t.Fatalf("WithTraceState: Unexpected context created: %s", cmp.Diff(modified, to))
- }
- }
- func TestLinkFromContext(t *testing.T) {
- k1v1 := attribute.String("key1", "value1")
- spanCtx := SpanContext{traceID: TraceID([16]byte{1}), remote: true}
- receiverCtx := ContextWithRemoteSpanContext(context.Background(), spanCtx)
- link := LinkFromContext(receiverCtx, k1v1)
- if !assertSpanContextEqual(link.SpanContext, spanCtx) {
- t.Fatalf("LinkFromContext: Unexpected context created: %s", cmp.Diff(link.SpanContext, spanCtx))
- }
- assert.Equal(t, link.Attributes[0], k1v1)
- }
|