...

Source file src/go.opentelemetry.io/otel/propagation/trace_context_test.go

Documentation: go.opentelemetry.io/otel/propagation

     1  // Copyright The OpenTelemetry Authors
     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  
    15  package propagation_test
    16  
    17  import (
    18  	"context"
    19  	"net/http"
    20  	"testing"
    21  
    22  	"github.com/stretchr/testify/assert"
    23  	"github.com/stretchr/testify/require"
    24  
    25  	"go.opentelemetry.io/otel/propagation"
    26  	"go.opentelemetry.io/otel/trace"
    27  )
    28  
    29  var (
    30  	traceparent = http.CanonicalHeaderKey("traceparent")
    31  	tracestate  = http.CanonicalHeaderKey("tracestate")
    32  
    33  	prop = propagation.TraceContext{}
    34  )
    35  
    36  type testcase struct {
    37  	name   string
    38  	header http.Header
    39  	sc     trace.SpanContext
    40  }
    41  
    42  func TestExtractValidTraceContext(t *testing.T) {
    43  	stateStr := "key1=value1,key2=value2"
    44  	state, err := trace.ParseTraceState(stateStr)
    45  	require.NoError(t, err)
    46  
    47  	tests := []testcase{
    48  		{
    49  			name: "not sampled",
    50  			header: http.Header{
    51  				traceparent: []string{"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00"},
    52  			},
    53  			sc: trace.NewSpanContext(trace.SpanContextConfig{
    54  				TraceID: traceID,
    55  				SpanID:  spanID,
    56  				Remote:  true,
    57  			}),
    58  		},
    59  		{
    60  			name: "sampled",
    61  			header: http.Header{
    62  				traceparent: []string{"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"},
    63  			},
    64  			sc: trace.NewSpanContext(trace.SpanContextConfig{
    65  				TraceID:    traceID,
    66  				SpanID:     spanID,
    67  				TraceFlags: trace.FlagsSampled,
    68  				Remote:     true,
    69  			}),
    70  		},
    71  		{
    72  			name: "valid tracestate",
    73  			header: http.Header{
    74  				traceparent: []string{"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00"},
    75  				tracestate:  []string{stateStr},
    76  			},
    77  			sc: trace.NewSpanContext(trace.SpanContextConfig{
    78  				TraceID:    traceID,
    79  				SpanID:     spanID,
    80  				TraceState: state,
    81  				Remote:     true,
    82  			}),
    83  		},
    84  		{
    85  			name: "invalid tracestate preserves traceparent",
    86  			header: http.Header{
    87  				traceparent: []string{"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00"},
    88  				tracestate:  []string{"invalid$@#=invalid"},
    89  			},
    90  			sc: trace.NewSpanContext(trace.SpanContextConfig{
    91  				TraceID: traceID,
    92  				SpanID:  spanID,
    93  				Remote:  true,
    94  			}),
    95  		},
    96  		{
    97  			name: "future version not sampled",
    98  			header: http.Header{
    99  				traceparent: []string{"02-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00"},
   100  			},
   101  			sc: trace.NewSpanContext(trace.SpanContextConfig{
   102  				TraceID: traceID,
   103  				SpanID:  spanID,
   104  				Remote:  true,
   105  			}),
   106  		},
   107  		{
   108  			name: "future version sampled",
   109  			header: http.Header{
   110  				traceparent: []string{"02-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"},
   111  			},
   112  			sc: trace.NewSpanContext(trace.SpanContextConfig{
   113  				TraceID:    traceID,
   114  				SpanID:     spanID,
   115  				TraceFlags: trace.FlagsSampled,
   116  				Remote:     true,
   117  			}),
   118  		},
   119  		{
   120  			name: "future version sample bit set",
   121  			header: http.Header{
   122  				traceparent: []string{"02-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-09"},
   123  			},
   124  			sc: trace.NewSpanContext(trace.SpanContextConfig{
   125  				TraceID:    traceID,
   126  				SpanID:     spanID,
   127  				TraceFlags: trace.FlagsSampled,
   128  				Remote:     true,
   129  			}),
   130  		},
   131  		{
   132  			name: "future version sample bit not set",
   133  			header: http.Header{
   134  				traceparent: []string{"02-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-08"},
   135  			},
   136  			sc: trace.NewSpanContext(trace.SpanContextConfig{
   137  				TraceID: traceID,
   138  				SpanID:  spanID,
   139  				Remote:  true,
   140  			}),
   141  		},
   142  		{
   143  			name: "future version additional data",
   144  			header: http.Header{
   145  				traceparent: []string{"02-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00-XYZxsf09"},
   146  			},
   147  			sc: trace.NewSpanContext(trace.SpanContextConfig{
   148  				TraceID: traceID,
   149  				SpanID:  spanID,
   150  				Remote:  true,
   151  			}),
   152  		},
   153  		{
   154  			name: "B3 format ending in dash",
   155  			header: http.Header{
   156  				traceparent: []string{"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00-"},
   157  			},
   158  			sc: trace.NewSpanContext(trace.SpanContextConfig{
   159  				TraceID: traceID,
   160  				SpanID:  spanID,
   161  				Remote:  true,
   162  			}),
   163  		},
   164  		{
   165  			name: "future version B3 format ending in dash",
   166  			header: http.Header{
   167  				traceparent: []string{"03-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00-"},
   168  			},
   169  			sc: trace.NewSpanContext(trace.SpanContextConfig{
   170  				TraceID: traceID,
   171  				SpanID:  spanID,
   172  				Remote:  true,
   173  			}),
   174  		},
   175  	}
   176  
   177  	for _, tc := range tests {
   178  		t.Run(tc.name, func(t *testing.T) {
   179  			ctx := context.Background()
   180  			ctx = prop.Extract(ctx, propagation.HeaderCarrier(tc.header))
   181  			assert.Equal(t, tc.sc, trace.SpanContextFromContext(ctx))
   182  		})
   183  	}
   184  }
   185  
   186  func TestExtractInvalidTraceContextFromHTTPReq(t *testing.T) {
   187  	tests := []struct {
   188  		name   string
   189  		header string
   190  	}{
   191  		{
   192  			name:   "wrong version length",
   193  			header: "0000-00000000000000000000000000000000-0000000000000000-01",
   194  		},
   195  		{
   196  			name:   "wrong trace ID length",
   197  			header: "00-ab00000000000000000000000000000000-cd00000000000000-01",
   198  		},
   199  		{
   200  			name:   "wrong span ID length",
   201  			header: "00-ab000000000000000000000000000000-cd0000000000000000-01",
   202  		},
   203  		{
   204  			name:   "wrong trace flag length",
   205  			header: "00-ab000000000000000000000000000000-cd00000000000000-0100",
   206  		},
   207  		{
   208  			name:   "bogus version",
   209  			header: "qw-00000000000000000000000000000000-0000000000000000-01",
   210  		},
   211  		{
   212  			name:   "bogus trace ID",
   213  			header: "00-qw000000000000000000000000000000-cd00000000000000-01",
   214  		},
   215  		{
   216  			name:   "bogus span ID",
   217  			header: "00-ab000000000000000000000000000000-qw00000000000000-01",
   218  		},
   219  		{
   220  			name:   "bogus trace flag",
   221  			header: "00-ab000000000000000000000000000000-cd00000000000000-qw",
   222  		},
   223  		{
   224  			name:   "upper case version",
   225  			header: "A0-00000000000000000000000000000000-0000000000000000-01",
   226  		},
   227  		{
   228  			name:   "upper case trace ID",
   229  			header: "00-AB000000000000000000000000000000-cd00000000000000-01",
   230  		},
   231  		{
   232  			name:   "upper case span ID",
   233  			header: "00-ab000000000000000000000000000000-CD00000000000000-01",
   234  		},
   235  		{
   236  			name:   "upper case trace flag",
   237  			header: "00-ab000000000000000000000000000000-cd00000000000000-A1",
   238  		},
   239  		{
   240  			name:   "zero trace ID and span ID",
   241  			header: "00-00000000000000000000000000000000-0000000000000000-01",
   242  		},
   243  		{
   244  			name:   "trace-flag unused bits set",
   245  			header: "00-ab000000000000000000000000000000-cd00000000000000-09",
   246  		},
   247  		{
   248  			name:   "missing options",
   249  			header: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7",
   250  		},
   251  		{
   252  			name:   "empty options",
   253  			header: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-",
   254  		},
   255  	}
   256  
   257  	empty := trace.SpanContext{}
   258  	for _, tt := range tests {
   259  		t.Run(tt.name, func(t *testing.T) {
   260  			h := http.Header{traceparent: []string{tt.header}}
   261  			ctx := context.Background()
   262  			ctx = prop.Extract(ctx, propagation.HeaderCarrier(h))
   263  
   264  			// Failure to extract needs to result in no SpanContext being set.
   265  			// This cannot be directly measured, but we can check that an
   266  			// zero-value SpanContext is returned from SpanContextFromContext.
   267  			assert.Equal(t, empty, trace.SpanContextFromContext(ctx))
   268  		})
   269  	}
   270  }
   271  
   272  func TestInjectValidTraceContext(t *testing.T) {
   273  	stateStr := "key1=value1,key2=value2"
   274  	state, err := trace.ParseTraceState(stateStr)
   275  	require.NoError(t, err)
   276  
   277  	tests := []testcase{
   278  		{
   279  			name: "not sampled",
   280  			header: http.Header{
   281  				traceparent: []string{"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00"},
   282  			},
   283  			sc: trace.NewSpanContext(trace.SpanContextConfig{
   284  				TraceID: traceID,
   285  				SpanID:  spanID,
   286  				Remote:  true,
   287  			}),
   288  		},
   289  		{
   290  			name: "sampled",
   291  			header: http.Header{
   292  				traceparent: []string{"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"},
   293  			},
   294  			sc: trace.NewSpanContext(trace.SpanContextConfig{
   295  				TraceID:    traceID,
   296  				SpanID:     spanID,
   297  				TraceFlags: trace.FlagsSampled,
   298  				Remote:     true,
   299  			}),
   300  		},
   301  		{
   302  			name: "unsupported trace flag bits dropped",
   303  			header: http.Header{
   304  				traceparent: []string{"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"},
   305  			},
   306  			sc: trace.NewSpanContext(trace.SpanContextConfig{
   307  				TraceID:    traceID,
   308  				SpanID:     spanID,
   309  				TraceFlags: 0xff,
   310  				Remote:     true,
   311  			}),
   312  		},
   313  		{
   314  			name: "with tracestate",
   315  			header: http.Header{
   316  				traceparent: []string{"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00"},
   317  				tracestate:  []string{stateStr},
   318  			},
   319  			sc: trace.NewSpanContext(trace.SpanContextConfig{
   320  				TraceID:    traceID,
   321  				SpanID:     spanID,
   322  				TraceState: state,
   323  				Remote:     true,
   324  			}),
   325  		},
   326  	}
   327  
   328  	for _, tc := range tests {
   329  		t.Run(tc.name, func(t *testing.T) {
   330  			ctx := context.Background()
   331  			ctx = trace.ContextWithRemoteSpanContext(ctx, tc.sc)
   332  
   333  			h := http.Header{}
   334  			prop.Inject(ctx, propagation.HeaderCarrier(h))
   335  			assert.Equal(t, tc.header, h)
   336  		})
   337  	}
   338  }
   339  
   340  func TestInvalidSpanContextDropped(t *testing.T) {
   341  	invalidSC := trace.SpanContext{}
   342  	require.False(t, invalidSC.IsValid())
   343  	ctx := trace.ContextWithRemoteSpanContext(context.Background(), invalidSC)
   344  
   345  	header := http.Header{}
   346  	propagation.TraceContext{}.Inject(ctx, propagation.HeaderCarrier(header))
   347  	assert.Equal(t, "", header.Get("traceparent"), "injected invalid SpanContext")
   348  }
   349  
   350  func TestTraceContextFields(t *testing.T) {
   351  	expected := []string{"traceparent", "tracestate"}
   352  	assert.Equal(t, expected, propagation.TraceContext{}.Fields())
   353  }
   354  

View as plain text