You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
356 lines
9.2 KiB
356 lines
9.2 KiB
package trace |
|
|
|
import ( |
|
"context" |
|
"testing" |
|
|
|
"github.com/stretchr/testify/assert" |
|
"github.com/stretchr/testify/require" |
|
"go.opentelemetry.io/otel" |
|
"go.opentelemetry.io/otel/propagation" |
|
"go.opentelemetry.io/otel/trace" |
|
"google.golang.org/grpc/metadata" |
|
) |
|
|
|
const ( |
|
traceIDStr = "4bf92f3577b34da6a3ce929d0e0e4736" |
|
spanIDStr = "00f067aa0ba902b7" |
|
) |
|
|
|
var ( |
|
traceID = mustTraceIDFromHex(traceIDStr) |
|
spanID = mustSpanIDFromHex(spanIDStr) |
|
) |
|
|
|
func mustTraceIDFromHex(s string) (t trace.TraceID) { |
|
var err error |
|
t, err = trace.TraceIDFromHex(s) |
|
if err != nil { |
|
panic(err) |
|
} |
|
return |
|
} |
|
|
|
func mustSpanIDFromHex(s string) (t trace.SpanID) { |
|
var err error |
|
t, err = trace.SpanIDFromHex(s) |
|
if err != nil { |
|
panic(err) |
|
} |
|
return |
|
} |
|
|
|
func TestExtractValidTraceContext(t *testing.T) { |
|
stateStr := "key1=value1,key2=value2" |
|
state, err := trace.ParseTraceState(stateStr) |
|
require.NoError(t, err) |
|
|
|
tests := []struct { |
|
name string |
|
traceparent string |
|
tracestate string |
|
sc trace.SpanContext |
|
}{ |
|
{ |
|
name: "not sampled", |
|
traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00", |
|
sc: trace.NewSpanContext(trace.SpanContextConfig{ |
|
TraceID: traceID, |
|
SpanID: spanID, |
|
Remote: true, |
|
}), |
|
}, |
|
{ |
|
name: "sampled", |
|
traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01", |
|
sc: trace.NewSpanContext(trace.SpanContextConfig{ |
|
TraceID: traceID, |
|
SpanID: spanID, |
|
TraceFlags: trace.FlagsSampled, |
|
Remote: true, |
|
}), |
|
}, |
|
{ |
|
name: "valid tracestate", |
|
traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00", |
|
tracestate: stateStr, |
|
sc: trace.NewSpanContext(trace.SpanContextConfig{ |
|
TraceID: traceID, |
|
SpanID: spanID, |
|
TraceState: state, |
|
Remote: true, |
|
}), |
|
}, |
|
{ |
|
name: "invalid tracestate perserves traceparent", |
|
traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00", |
|
tracestate: "invalid$@#=invalid", |
|
sc: trace.NewSpanContext(trace.SpanContextConfig{ |
|
TraceID: traceID, |
|
SpanID: spanID, |
|
Remote: true, |
|
}), |
|
}, |
|
{ |
|
name: "future version not sampled", |
|
traceparent: "02-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00", |
|
sc: trace.NewSpanContext(trace.SpanContextConfig{ |
|
TraceID: traceID, |
|
SpanID: spanID, |
|
Remote: true, |
|
}), |
|
}, |
|
{ |
|
name: "future version sampled", |
|
traceparent: "02-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01", |
|
sc: trace.NewSpanContext(trace.SpanContextConfig{ |
|
TraceID: traceID, |
|
SpanID: spanID, |
|
TraceFlags: trace.FlagsSampled, |
|
Remote: true, |
|
}), |
|
}, |
|
{ |
|
name: "future version sample bit set", |
|
traceparent: "02-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-09", |
|
sc: trace.NewSpanContext(trace.SpanContextConfig{ |
|
TraceID: traceID, |
|
SpanID: spanID, |
|
TraceFlags: trace.FlagsSampled, |
|
Remote: true, |
|
}), |
|
}, |
|
{ |
|
name: "future version sample bit not set", |
|
traceparent: "02-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-08", |
|
sc: trace.NewSpanContext(trace.SpanContextConfig{ |
|
TraceID: traceID, |
|
SpanID: spanID, |
|
Remote: true, |
|
}), |
|
}, |
|
{ |
|
name: "future version additional data", |
|
traceparent: "02-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00-XYZxsf09", |
|
sc: trace.NewSpanContext(trace.SpanContextConfig{ |
|
TraceID: traceID, |
|
SpanID: spanID, |
|
Remote: true, |
|
}), |
|
}, |
|
{ |
|
name: "B3 format ending in dash", |
|
traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00-", |
|
sc: trace.NewSpanContext(trace.SpanContextConfig{ |
|
TraceID: traceID, |
|
SpanID: spanID, |
|
Remote: true, |
|
}), |
|
}, |
|
{ |
|
name: "future version B3 format ending in dash", |
|
traceparent: "03-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00-", |
|
sc: trace.NewSpanContext(trace.SpanContextConfig{ |
|
TraceID: traceID, |
|
SpanID: spanID, |
|
Remote: true, |
|
}), |
|
}, |
|
} |
|
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator( |
|
propagation.TraceContext{}, propagation.Baggage{})) |
|
propagator := otel.GetTextMapPropagator() |
|
|
|
for _, tt := range tests { |
|
t.Run(tt.name, func(t *testing.T) { |
|
ctx := context.Background() |
|
md := metadata.MD{} |
|
md.Set("traceparent", tt.traceparent) |
|
md.Set("tracestate", tt.tracestate) |
|
_, spanCtx := Extract(ctx, propagator, &md) |
|
assert.Equal(t, tt.sc, spanCtx) |
|
}) |
|
} |
|
} |
|
|
|
func TestExtractInvalidTraceContext(t *testing.T) { |
|
tests := []struct { |
|
name string |
|
header string |
|
}{ |
|
{ |
|
name: "wrong version length", |
|
header: "0000-00000000000000000000000000000000-0000000000000000-01", |
|
}, |
|
{ |
|
name: "wrong trace ID length", |
|
header: "00-ab00000000000000000000000000000000-cd00000000000000-01", |
|
}, |
|
{ |
|
name: "wrong span ID length", |
|
header: "00-ab000000000000000000000000000000-cd0000000000000000-01", |
|
}, |
|
{ |
|
name: "wrong trace flag length", |
|
header: "00-ab000000000000000000000000000000-cd00000000000000-0100", |
|
}, |
|
{ |
|
name: "bogus version", |
|
header: "qw-00000000000000000000000000000000-0000000000000000-01", |
|
}, |
|
{ |
|
name: "bogus trace ID", |
|
header: "00-qw000000000000000000000000000000-cd00000000000000-01", |
|
}, |
|
{ |
|
name: "bogus span ID", |
|
header: "00-ab000000000000000000000000000000-qw00000000000000-01", |
|
}, |
|
{ |
|
name: "bogus trace flag", |
|
header: "00-ab000000000000000000000000000000-cd00000000000000-qw", |
|
}, |
|
{ |
|
name: "upper case version", |
|
header: "A0-00000000000000000000000000000000-0000000000000000-01", |
|
}, |
|
{ |
|
name: "upper case trace ID", |
|
header: "00-AB000000000000000000000000000000-cd00000000000000-01", |
|
}, |
|
{ |
|
name: "upper case span ID", |
|
header: "00-ab000000000000000000000000000000-CD00000000000000-01", |
|
}, |
|
{ |
|
name: "upper case trace flag", |
|
header: "00-ab000000000000000000000000000000-cd00000000000000-A1", |
|
}, |
|
{ |
|
name: "zero trace ID and span ID", |
|
header: "00-00000000000000000000000000000000-0000000000000000-01", |
|
}, |
|
{ |
|
name: "trace-flag unused bits set", |
|
header: "00-ab000000000000000000000000000000-cd00000000000000-09", |
|
}, |
|
{ |
|
name: "missing options", |
|
header: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7", |
|
}, |
|
{ |
|
name: "empty options", |
|
header: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-", |
|
}, |
|
} |
|
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator( |
|
propagation.TraceContext{}, propagation.Baggage{})) |
|
propagator := otel.GetTextMapPropagator() |
|
|
|
for _, tt := range tests { |
|
t.Run(tt.name, func(t *testing.T) { |
|
ctx := context.Background() |
|
md := metadata.MD{} |
|
md.Set("traceparent", tt.header) |
|
_, spanCtx := Extract(ctx, propagator, &md) |
|
assert.Equal(t, trace.SpanContext{}, spanCtx) |
|
}) |
|
} |
|
} |
|
|
|
func TestInjectValidTraceContext(t *testing.T) { |
|
stateStr := "key1=value1,key2=value2" |
|
state, err := trace.ParseTraceState(stateStr) |
|
require.NoError(t, err) |
|
|
|
tests := []struct { |
|
name string |
|
traceparent string |
|
tracestate string |
|
sc trace.SpanContext |
|
}{ |
|
{ |
|
name: "not sampled", |
|
traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00", |
|
sc: trace.NewSpanContext(trace.SpanContextConfig{ |
|
TraceID: traceID, |
|
SpanID: spanID, |
|
Remote: true, |
|
}), |
|
}, |
|
{ |
|
name: "sampled", |
|
traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01", |
|
sc: trace.NewSpanContext(trace.SpanContextConfig{ |
|
TraceID: traceID, |
|
SpanID: spanID, |
|
TraceFlags: trace.FlagsSampled, |
|
Remote: true, |
|
}), |
|
}, |
|
{ |
|
name: "unsupported trace flag bits dropped", |
|
traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01", |
|
sc: trace.NewSpanContext(trace.SpanContextConfig{ |
|
TraceID: traceID, |
|
SpanID: spanID, |
|
TraceFlags: 0xff, |
|
Remote: true, |
|
}), |
|
}, |
|
{ |
|
name: "with tracestate", |
|
traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00", |
|
tracestate: stateStr, |
|
sc: trace.NewSpanContext(trace.SpanContextConfig{ |
|
TraceID: traceID, |
|
SpanID: spanID, |
|
TraceState: state, |
|
Remote: true, |
|
}), |
|
}, |
|
} |
|
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator( |
|
propagation.TraceContext{}, propagation.Baggage{})) |
|
propagator := otel.GetTextMapPropagator() |
|
|
|
for _, tt := range tests { |
|
t.Run(tt.name, func(t *testing.T) { |
|
ctx := context.Background() |
|
ctx = trace.ContextWithRemoteSpanContext(ctx, tt.sc) |
|
|
|
want := metadata.MD{} |
|
want.Set("traceparent", tt.traceparent) |
|
if len(tt.tracestate) > 0 { |
|
want.Set("tracestate", tt.tracestate) |
|
} |
|
|
|
md := metadata.MD{} |
|
Inject(ctx, propagator, &md) |
|
assert.Equal(t, want, md) |
|
|
|
mm := &metadataSupplier{ |
|
metadata: &md, |
|
} |
|
assert.NotEmpty(t, mm.Keys()) |
|
}) |
|
} |
|
} |
|
|
|
func TestInvalidSpanContextDropped(t *testing.T) { |
|
invalidSC := trace.SpanContext{} |
|
require.False(t, invalidSC.IsValid()) |
|
ctx := trace.ContextWithRemoteSpanContext(context.Background(), invalidSC) |
|
|
|
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator( |
|
propagation.TraceContext{}, propagation.Baggage{})) |
|
propagator := otel.GetTextMapPropagator() |
|
|
|
md := metadata.MD{} |
|
Inject(ctx, propagator, &md) |
|
mm := &metadataSupplier{ |
|
metadata: &md, |
|
} |
|
assert.Empty(t, mm.Keys()) |
|
assert.Equal(t, "", mm.Get("traceparent"), "injected invalid SpanContext") |
|
}
|
|
|