github.com/lingyao2333/mo-zero@v1.4.1/core/trace/tracer_test.go (about) 1 package trace 2 3 import ( 4 "context" 5 "testing" 6 7 "github.com/stretchr/testify/assert" 8 "github.com/stretchr/testify/require" 9 "go.opentelemetry.io/otel" 10 "go.opentelemetry.io/otel/propagation" 11 "go.opentelemetry.io/otel/trace" 12 "google.golang.org/grpc/metadata" 13 ) 14 15 const ( 16 traceIDStr = "4bf92f3577b34da6a3ce929d0e0e4736" 17 spanIDStr = "00f067aa0ba902b7" 18 ) 19 20 var ( 21 traceID = mustTraceIDFromHex(traceIDStr) 22 spanID = mustSpanIDFromHex(spanIDStr) 23 ) 24 25 func mustTraceIDFromHex(s string) (t trace.TraceID) { 26 var err error 27 t, err = trace.TraceIDFromHex(s) 28 if err != nil { 29 panic(err) 30 } 31 return 32 } 33 34 func mustSpanIDFromHex(s string) (t trace.SpanID) { 35 var err error 36 t, err = trace.SpanIDFromHex(s) 37 if err != nil { 38 panic(err) 39 } 40 return 41 } 42 43 func TestExtractValidTraceContext(t *testing.T) { 44 stateStr := "key1=value1,key2=value2" 45 state, err := trace.ParseTraceState(stateStr) 46 require.NoError(t, err) 47 48 tests := []struct { 49 name string 50 traceparent string 51 tracestate string 52 sc trace.SpanContext 53 }{ 54 { 55 name: "not sampled", 56 traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00", 57 sc: trace.NewSpanContext(trace.SpanContextConfig{ 58 TraceID: traceID, 59 SpanID: spanID, 60 Remote: true, 61 }), 62 }, 63 { 64 name: "sampled", 65 traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01", 66 sc: trace.NewSpanContext(trace.SpanContextConfig{ 67 TraceID: traceID, 68 SpanID: spanID, 69 TraceFlags: trace.FlagsSampled, 70 Remote: true, 71 }), 72 }, 73 { 74 name: "valid tracestate", 75 traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00", 76 tracestate: stateStr, 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 perserves traceparent", 86 traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00", 87 tracestate: "invalid$@#=invalid", 88 sc: trace.NewSpanContext(trace.SpanContextConfig{ 89 TraceID: traceID, 90 SpanID: spanID, 91 Remote: true, 92 }), 93 }, 94 { 95 name: "future version not sampled", 96 traceparent: "02-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00", 97 sc: trace.NewSpanContext(trace.SpanContextConfig{ 98 TraceID: traceID, 99 SpanID: spanID, 100 Remote: true, 101 }), 102 }, 103 { 104 name: "future version sampled", 105 traceparent: "02-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01", 106 sc: trace.NewSpanContext(trace.SpanContextConfig{ 107 TraceID: traceID, 108 SpanID: spanID, 109 TraceFlags: trace.FlagsSampled, 110 Remote: true, 111 }), 112 }, 113 { 114 name: "future version sample bit set", 115 traceparent: "02-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-09", 116 sc: trace.NewSpanContext(trace.SpanContextConfig{ 117 TraceID: traceID, 118 SpanID: spanID, 119 TraceFlags: trace.FlagsSampled, 120 Remote: true, 121 }), 122 }, 123 { 124 name: "future version sample bit not set", 125 traceparent: "02-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-08", 126 sc: trace.NewSpanContext(trace.SpanContextConfig{ 127 TraceID: traceID, 128 SpanID: spanID, 129 Remote: true, 130 }), 131 }, 132 { 133 name: "future version additional data", 134 traceparent: "02-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00-XYZxsf09", 135 sc: trace.NewSpanContext(trace.SpanContextConfig{ 136 TraceID: traceID, 137 SpanID: spanID, 138 Remote: true, 139 }), 140 }, 141 { 142 name: "B3 format ending in dash", 143 traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00-", 144 sc: trace.NewSpanContext(trace.SpanContextConfig{ 145 TraceID: traceID, 146 SpanID: spanID, 147 Remote: true, 148 }), 149 }, 150 { 151 name: "future version B3 format ending in dash", 152 traceparent: "03-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00-", 153 sc: trace.NewSpanContext(trace.SpanContextConfig{ 154 TraceID: traceID, 155 SpanID: spanID, 156 Remote: true, 157 }), 158 }, 159 } 160 otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator( 161 propagation.TraceContext{}, propagation.Baggage{})) 162 propagator := otel.GetTextMapPropagator() 163 164 for _, tt := range tests { 165 t.Run(tt.name, func(t *testing.T) { 166 ctx := context.Background() 167 md := metadata.MD{} 168 md.Set("traceparent", tt.traceparent) 169 md.Set("tracestate", tt.tracestate) 170 _, spanCtx := Extract(ctx, propagator, &md) 171 assert.Equal(t, tt.sc, spanCtx) 172 }) 173 } 174 } 175 176 func TestExtractInvalidTraceContext(t *testing.T) { 177 tests := []struct { 178 name string 179 header string 180 }{ 181 { 182 name: "wrong version length", 183 header: "0000-00000000000000000000000000000000-0000000000000000-01", 184 }, 185 { 186 name: "wrong trace ID length", 187 header: "00-ab00000000000000000000000000000000-cd00000000000000-01", 188 }, 189 { 190 name: "wrong span ID length", 191 header: "00-ab000000000000000000000000000000-cd0000000000000000-01", 192 }, 193 { 194 name: "wrong trace flag length", 195 header: "00-ab000000000000000000000000000000-cd00000000000000-0100", 196 }, 197 { 198 name: "bogus version", 199 header: "qw-00000000000000000000000000000000-0000000000000000-01", 200 }, 201 { 202 name: "bogus trace ID", 203 header: "00-qw000000000000000000000000000000-cd00000000000000-01", 204 }, 205 { 206 name: "bogus span ID", 207 header: "00-ab000000000000000000000000000000-qw00000000000000-01", 208 }, 209 { 210 name: "bogus trace flag", 211 header: "00-ab000000000000000000000000000000-cd00000000000000-qw", 212 }, 213 { 214 name: "upper case version", 215 header: "A0-00000000000000000000000000000000-0000000000000000-01", 216 }, 217 { 218 name: "upper case trace ID", 219 header: "00-AB000000000000000000000000000000-cd00000000000000-01", 220 }, 221 { 222 name: "upper case span ID", 223 header: "00-ab000000000000000000000000000000-CD00000000000000-01", 224 }, 225 { 226 name: "upper case trace flag", 227 header: "00-ab000000000000000000000000000000-cd00000000000000-A1", 228 }, 229 { 230 name: "zero trace ID and span ID", 231 header: "00-00000000000000000000000000000000-0000000000000000-01", 232 }, 233 { 234 name: "trace-flag unused bits set", 235 header: "00-ab000000000000000000000000000000-cd00000000000000-09", 236 }, 237 { 238 name: "missing options", 239 header: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7", 240 }, 241 { 242 name: "empty options", 243 header: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-", 244 }, 245 } 246 otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator( 247 propagation.TraceContext{}, propagation.Baggage{})) 248 propagator := otel.GetTextMapPropagator() 249 250 for _, tt := range tests { 251 t.Run(tt.name, func(t *testing.T) { 252 ctx := context.Background() 253 md := metadata.MD{} 254 md.Set("traceparent", tt.header) 255 _, spanCtx := Extract(ctx, propagator, &md) 256 assert.Equal(t, trace.SpanContext{}, spanCtx) 257 }) 258 } 259 } 260 261 func TestInjectValidTraceContext(t *testing.T) { 262 stateStr := "key1=value1,key2=value2" 263 state, err := trace.ParseTraceState(stateStr) 264 require.NoError(t, err) 265 266 tests := []struct { 267 name string 268 traceparent string 269 tracestate string 270 sc trace.SpanContext 271 }{ 272 { 273 name: "not sampled", 274 traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00", 275 sc: trace.NewSpanContext(trace.SpanContextConfig{ 276 TraceID: traceID, 277 SpanID: spanID, 278 Remote: true, 279 }), 280 }, 281 { 282 name: "sampled", 283 traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01", 284 sc: trace.NewSpanContext(trace.SpanContextConfig{ 285 TraceID: traceID, 286 SpanID: spanID, 287 TraceFlags: trace.FlagsSampled, 288 Remote: true, 289 }), 290 }, 291 { 292 name: "unsupported trace flag bits dropped", 293 traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01", 294 sc: trace.NewSpanContext(trace.SpanContextConfig{ 295 TraceID: traceID, 296 SpanID: spanID, 297 TraceFlags: 0xff, 298 Remote: true, 299 }), 300 }, 301 { 302 name: "with tracestate", 303 traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00", 304 tracestate: stateStr, 305 sc: trace.NewSpanContext(trace.SpanContextConfig{ 306 TraceID: traceID, 307 SpanID: spanID, 308 TraceState: state, 309 Remote: true, 310 }), 311 }, 312 } 313 otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator( 314 propagation.TraceContext{}, propagation.Baggage{})) 315 propagator := otel.GetTextMapPropagator() 316 317 for _, tt := range tests { 318 t.Run(tt.name, func(t *testing.T) { 319 ctx := context.Background() 320 ctx = trace.ContextWithRemoteSpanContext(ctx, tt.sc) 321 322 want := metadata.MD{} 323 want.Set("traceparent", tt.traceparent) 324 if len(tt.tracestate) > 0 { 325 want.Set("tracestate", tt.tracestate) 326 } 327 328 md := metadata.MD{} 329 Inject(ctx, propagator, &md) 330 assert.Equal(t, want, md) 331 332 mm := &metadataSupplier{ 333 metadata: &md, 334 } 335 assert.NotEmpty(t, mm.Keys()) 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 otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator( 346 propagation.TraceContext{}, propagation.Baggage{})) 347 propagator := otel.GetTextMapPropagator() 348 349 md := metadata.MD{} 350 Inject(ctx, propagator, &md) 351 mm := &metadataSupplier{ 352 metadata: &md, 353 } 354 assert.Empty(t, mm.Keys()) 355 assert.Equal(t, "", mm.Get("traceparent"), "injected invalid SpanContext") 356 }