google.golang.org/grpc@v1.72.2/stats/opentelemetry/grpc_trace_bin_propagator_test.go (about) 1 /* 2 * 3 * Copyright 2024 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 package opentelemetry 20 21 import ( 22 "context" 23 "testing" 24 25 "github.com/google/go-cmp/cmp" 26 oteltrace "go.opentelemetry.io/otel/trace" 27 "google.golang.org/grpc/metadata" 28 itracing "google.golang.org/grpc/stats/opentelemetry/internal/tracing" 29 ) 30 31 var validSpanContext = oteltrace.SpanContext{}.WithTraceID( 32 oteltrace.TraceID{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}).WithSpanID( 33 oteltrace.SpanID{17, 18, 19, 20, 21, 22, 23, 24}).WithTraceFlags( 34 oteltrace.TraceFlags(1)) 35 36 // TestInject_ValidSpanContext verifies that the GRPCTraceBinPropagator 37 // correctly injects a valid OpenTelemetry span context as `grpc-trace-bin` 38 // header in the provided carrier's context metadata. 39 // 40 // It verifies that if a valid span context is injected, same span context can 41 // can be retreived from the carrier's context metadata. 42 func (s) TestInject_ValidSpanContext(t *testing.T) { 43 p := GRPCTraceBinPropagator{} 44 ctx, cancel := context.WithCancel(context.Background()) 45 defer cancel() 46 c := itracing.NewOutgoingCarrier(ctx) 47 ctx = oteltrace.ContextWithSpanContext(ctx, validSpanContext) 48 49 p.Inject(ctx, c) 50 51 md, _ := metadata.FromOutgoingContext(c.Context()) 52 gotH := md.Get(grpcTraceBinHeaderKey) 53 if gotH[len(gotH)-1] == "" { 54 t.Fatalf("got empty value from Carrier's context metadata grpc-trace-bin header, want valid span context: %v", validSpanContext) 55 } 56 gotSC, ok := fromBinary([]byte(gotH[len(gotH)-1])) 57 if !ok { 58 t.Fatalf("got invalid span context %v from Carrier's context metadata grpc-trace-bin header, want valid span context: %v", gotSC, validSpanContext) 59 } 60 if cmp.Equal(validSpanContext, gotSC) { 61 t.Fatalf("got span context = %v, want span contexts %v", gotSC, validSpanContext) 62 } 63 } 64 65 // TestInject_InvalidSpanContext verifies that the GRPCTraceBinPropagator does 66 // not inject an invalid OpenTelemetry span context as `grpc-trace-bin` header 67 // in the provided carrier's context metadata. 68 // 69 // If an invalid span context is injected, it verifies that `grpc-trace-bin` 70 // header is not set in the carrier's context metadata. 71 func (s) TestInject_InvalidSpanContext(t *testing.T) { 72 p := GRPCTraceBinPropagator{} 73 ctx, cancel := context.WithCancel(context.Background()) 74 defer cancel() 75 c := itracing.NewOutgoingCarrier(ctx) 76 ctx = oteltrace.ContextWithSpanContext(ctx, oteltrace.SpanContext{}) 77 78 p.Inject(ctx, c) 79 80 md, _ := metadata.FromOutgoingContext(c.Context()) 81 if gotH := md.Get(grpcTraceBinHeaderKey); len(gotH) > 0 { 82 t.Fatalf("got %v value from Carrier's context metadata grpc-trace-bin header, want empty", gotH) 83 } 84 } 85 86 // TestExtract verifies that the GRPCTraceBinPropagator correctly extracts 87 // OpenTelemetry span context data from the provided context using carrier. 88 // 89 // If a valid span context was injected, it verifies same trace span context 90 // is extracted from carrier's metadata for `grpc-trace-bin` header key. 91 // 92 // If invalid span context was injected, it verifies that valid trace span 93 // context is not extracted. 94 func (s) TestExtract(t *testing.T) { 95 tests := []struct { 96 name string 97 wantSC oteltrace.SpanContext // expected span context from carrier 98 }{ 99 { 100 name: "valid OpenTelemetry span context", 101 wantSC: validSpanContext.WithRemote(true), 102 }, 103 { 104 name: "invalid OpenTelemetry span context", 105 wantSC: oteltrace.SpanContext{}, 106 }, 107 } 108 109 for _, test := range tests { 110 t.Run(test.name, func(t *testing.T) { 111 p := GRPCTraceBinPropagator{} 112 ctx, cancel := context.WithCancel(context.Background()) 113 defer cancel() 114 ctx = metadata.NewIncomingContext(ctx, metadata.MD{grpcTraceBinHeaderKey: []string{string(toBinary(test.wantSC))}}) 115 116 c := itracing.NewIncomingCarrier(ctx) 117 118 tCtx := p.Extract(ctx, c) 119 got := oteltrace.SpanContextFromContext(tCtx) 120 if !got.Equal(test.wantSC) { 121 t.Fatalf("got span context: %v, want span context: %v", got, test.wantSC) 122 } 123 }) 124 } 125 } 126 127 // TestBinary verifies that the toBinary() function correctly serializes a valid 128 // OpenTelemetry span context into its binary format representation. If span 129 // context is invalid, it verifies that serialization is nil. 130 func (s) TestToBinary(t *testing.T) { 131 tests := []struct { 132 name string 133 sc oteltrace.SpanContext 134 want []byte 135 }{ 136 { 137 name: "valid context", 138 sc: validSpanContext, 139 want: toBinary(validSpanContext), 140 }, 141 { 142 name: "zero value context", 143 sc: oteltrace.SpanContext{}, 144 want: nil, 145 }, 146 } 147 148 for _, test := range tests { 149 t.Run(test.name, func(t *testing.T) { 150 if got := toBinary(test.sc); !cmp.Equal(got, test.want) { 151 t.Fatalf("binary() = %v, want %v", got, test.want) 152 } 153 }) 154 } 155 } 156 157 // TestFromBinary verifies that the fromBinary() function correctly 158 // deserializes a binary format representation of a valid OpenTelemetry span 159 // context into its corresponding span context format. If span context's binary 160 // representation is invalid, it verifies that deserialization is zero value 161 // span context. 162 func (s) TestFromBinary(t *testing.T) { 163 tests := []struct { 164 name string 165 b []byte 166 want oteltrace.SpanContext 167 ok bool 168 }{ 169 { 170 name: "valid", 171 b: []byte{0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1, 17, 18, 19, 20, 21, 22, 23, 24, 2, 1}, 172 want: validSpanContext.WithRemote(true), 173 ok: true, 174 }, 175 { 176 name: "invalid length", 177 b: []byte{0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1, 17, 18, 19, 20, 21, 22, 23, 24, 2}, 178 want: oteltrace.SpanContext{}, 179 ok: false, 180 }, 181 { 182 name: "invalid version", 183 b: []byte{1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1, 17, 18, 19, 20, 21, 22, 23, 24, 2, 1}, 184 want: oteltrace.SpanContext{}, 185 ok: false, 186 }, 187 { 188 name: "invalid traceID field ID", 189 b: []byte{0, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1, 17, 18, 19, 20, 21, 22, 23, 24, 2, 1}, 190 want: oteltrace.SpanContext{}, 191 ok: false, 192 }, 193 { 194 name: "invalid spanID field ID", 195 b: []byte{0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0, 17, 18, 19, 20, 21, 22, 23, 24, 2, 1}, 196 want: oteltrace.SpanContext{}, 197 ok: false, 198 }, 199 { 200 name: "invalid traceFlags field ID", 201 b: []byte{0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1, 17, 18, 19, 20, 21, 22, 23, 24, 1, 1}, 202 want: oteltrace.SpanContext{}, 203 ok: false, 204 }, 205 } 206 207 for _, test := range tests { 208 t.Run(test.name, func(t *testing.T) { 209 got, ok := fromBinary(test.b) 210 if ok != test.ok { 211 t.Fatalf("fromBinary() ok = %v, want %v", ok, test.ok) 212 return 213 } 214 if !got.Equal(test.want) { 215 t.Fatalf("fromBinary() got = %v, want %v", got, test.want) 216 } 217 }) 218 } 219 }