package tracing import ( "context" "io" "net/http" "net/http/httptest" "testing" sdktrace "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/sdk/trace/tracetest" "go.opentelemetry.io/otel/trace" ) func TestRestConfigWrapper_parentGated(t *testing.T) { t.Parallel() var gotTraceparent string srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { gotTraceparent = r.Header.Get("traceparent") w.WriteHeader(http.StatusOK) })) t.Cleanup(srv.Close) sr := tracetest.NewSpanRecorder() tp := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(sr)) t.Cleanup(func() { _ = tp.Shutdown(context.Background()) }) rt := RestConfigWrapper(WithTracerProvider(tp))(http.DefaultTransport) do := func(ctx context.Context) { t.Helper() req, err := http.NewRequestWithContext(ctx, http.MethodGet, srv.URL, nil) if err != nil { t.Fatal(err) } resp, err := rt.RoundTrip(req) if err != nil { t.Fatal(err) } _, _ = io.Copy(io.Discard, resp.Body) _ = resp.Body.Close() } // No parent span: informer watches, leader election. Must not trace. do(context.Background()) if len(sr.Ended()) != 0 { t.Fatalf("request without parent span produced %d spans, want 0", len(sr.Ended())) } if gotTraceparent != "" { t.Fatalf("request without parent span injected traceparent %q", gotTraceparent) } // Under a parent span: one client child span, traceparent injected. ctx, parent := tp.Tracer("test").Start(context.Background(), "reconcile") do(ctx) parent.End() var client sdktrace.ReadOnlySpan for _, s := range sr.Ended() { if s.SpanKind() == trace.SpanKindClient { client = s } } if client == nil { t.Fatalf("no client span recorded, got %d spans", len(sr.Ended())) } if got := client.Parent().SpanID(); got != parent.SpanContext().SpanID() { t.Errorf("client span parent = %s, want %s", got, parent.SpanContext().SpanID()) } if gotTraceparent == "" { t.Error("traceparent header not injected under a parent span") } }