mirror of
https://github.com/go-sigma/sigma.git
synced 2026-10-28 14:34:34 +08:00
test(telemetry): cover context helpers and tracer initialization
Add tests for every context helper and for Init across disabled, http, grpc and invalid protocol configurations, raising coverage from 21.6% to 98.6%. This also surfaced a real bug: Init failed when tracing was enabled because resource.Default() (schema 1.43.0) conflicted with semconv.SchemaURL (1.26.0). Build the service name resource schemaless so Init succeeds.
This commit is contained in:
1 parent
fb7f20e8c4
commit
9a7e6c8655
3 files changed
+218
-2
No files matched your search
@@ -15,18 +15,136 @@
|
||||
package telemetry
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json/v2"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
||||
)
|
||||
|
||||
func TestInjectCarrierClearsStalePropagationHeaders(t *testing.T) {
|
||||
// setPropagator installs the composite propagator the package expects and
|
||||
// restores the previous one when the test finishes.
|
||||
func setPropagator(t *testing.T) {
|
||||
t.Helper()
|
||||
prev := otel.GetTextMapPropagator()
|
||||
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(
|
||||
propagation.TraceContext{},
|
||||
propagation.Baggage{},
|
||||
))
|
||||
t.Cleanup(func() { otel.SetTextMapPropagator(prev) })
|
||||
}
|
||||
|
||||
// recordingContext returns a context carrying a sampled span and the trace id
|
||||
// of that span.
|
||||
func recordingContext(t *testing.T) (context.Context, string) {
|
||||
t.Helper()
|
||||
tp := sdktrace.NewTracerProvider()
|
||||
t.Cleanup(func() { _ = tp.Shutdown(context.Background()) })
|
||||
|
||||
ctx, span := tp.Tracer("telemetry-test").Start(context.Background(), "op")
|
||||
t.Cleanup(func() { span.End() })
|
||||
return ctx, span.SpanContext().TraceID().String()
|
||||
}
|
||||
|
||||
func TestTraceIDFromContext(t *testing.T) {
|
||||
require.Empty(t, TraceIDFromContext(context.Background()))
|
||||
|
||||
ctx, want := recordingContext(t)
|
||||
require.Equal(t, want, TraceIDFromContext(ctx))
|
||||
}
|
||||
|
||||
func TestCarrierFromContext(t *testing.T) {
|
||||
setPropagator(t)
|
||||
|
||||
require.Nil(t, CarrierFromContext(context.Background()))
|
||||
|
||||
ctx, want := recordingContext(t)
|
||||
carrier := CarrierFromContext(ctx)
|
||||
require.NotNil(t, carrier)
|
||||
require.Contains(t, carrier, "traceparent")
|
||||
require.Equal(t, want, TraceIDFromCarrier(carrier))
|
||||
}
|
||||
|
||||
func TestCarrierFromContextWithoutPropagator(t *testing.T) {
|
||||
prev := otel.GetTextMapPropagator()
|
||||
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator())
|
||||
t.Cleanup(func() { otel.SetTextMapPropagator(prev) })
|
||||
|
||||
ctx, _ := recordingContext(t)
|
||||
require.Nil(t, CarrierFromContext(ctx))
|
||||
}
|
||||
|
||||
func TestContextWithCarrier(t *testing.T) {
|
||||
setPropagator(t)
|
||||
|
||||
base := context.Background()
|
||||
require.Equal(t, base, ContextWithCarrier(base, nil))
|
||||
require.Equal(t, base, ContextWithCarrier(base, map[string]string{}))
|
||||
|
||||
ctx, want := recordingContext(t)
|
||||
got := TraceIDFromContext(ContextWithCarrier(base, CarrierFromContext(ctx)))
|
||||
require.Equal(t, want, got)
|
||||
}
|
||||
|
||||
func TestTraceIDFromCarrier(t *testing.T) {
|
||||
setPropagator(t)
|
||||
|
||||
require.Empty(t, TraceIDFromCarrier(nil))
|
||||
|
||||
ctx, want := recordingContext(t)
|
||||
require.Equal(t, want, TraceIDFromCarrier(CarrierFromContext(ctx)))
|
||||
}
|
||||
|
||||
func TestTraceIDFromCarrierBytes(t *testing.T) {
|
||||
setPropagator(t)
|
||||
|
||||
require.Empty(t, TraceIDFromCarrierBytes([]byte("not-json")))
|
||||
|
||||
ctx, want := recordingContext(t)
|
||||
data, err := json.Marshal(CarrierFromContext(ctx))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, want, TraceIDFromCarrierBytes(data))
|
||||
}
|
||||
|
||||
func TestClearCarrier(t *testing.T) {
|
||||
setPropagator(t)
|
||||
|
||||
empty := map[string]string{}
|
||||
require.Equal(t, empty, ClearCarrier(empty))
|
||||
|
||||
carrier := map[string]string{
|
||||
"Traceparent": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
|
||||
"BAGGAGE": "hello=world",
|
||||
"tracestate": "vendor=value",
|
||||
"user-agent": "sigma",
|
||||
}
|
||||
require.Equal(t, map[string]string{"user-agent": "sigma"}, ClearCarrier(carrier))
|
||||
}
|
||||
|
||||
func TestInjectCarrier(t *testing.T) {
|
||||
setPropagator(t)
|
||||
|
||||
// Without an active span, stale propagation headers are dropped.
|
||||
got := InjectCarrier(context.Background(), map[string]string{
|
||||
"traceparent": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
|
||||
"user-agent": "sigma",
|
||||
})
|
||||
require.Equal(t, map[string]string{"user-agent": "sigma"}, got)
|
||||
|
||||
// Without an active span and a nil carrier, nothing is created.
|
||||
require.Nil(t, InjectCarrier(context.Background(), nil))
|
||||
|
||||
// With an active span and a nil carrier, a new carrier is created.
|
||||
ctx, want := recordingContext(t)
|
||||
got = InjectCarrier(ctx, nil)
|
||||
require.Equal(t, want, TraceIDFromCarrier(got))
|
||||
}
|
||||
|
||||
func TestInjectCarrierClearsStalePropagationHeaders(t *testing.T) {
|
||||
setPropagator(t)
|
||||
|
||||
carrier := map[string]string{
|
||||
"Traceparent": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
|
||||
|
||||
@@ -68,7 +68,7 @@ func Init(cfg *config.Configuration) (func(context.Context) error, error) {
|
||||
// 2. Resource (service.name, etc.)
|
||||
res, err := resource.Merge(
|
||||
resource.Default(),
|
||||
resource.NewWithAttributes(semconv.SchemaURL,
|
||||
resource.NewSchemaless(
|
||||
semconv.ServiceName(serviceName),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
// Copyright 2026 sigma
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package telemetry
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.opentelemetry.io/otel"
|
||||
|
||||
"github.com/go-sigma/sigma/pkg/config"
|
||||
)
|
||||
|
||||
func TestInit(t *testing.T) {
|
||||
prevTP := otel.GetTracerProvider()
|
||||
prevPropagator := otel.GetTextMapPropagator()
|
||||
t.Cleanup(func() {
|
||||
otel.SetTracerProvider(prevTP)
|
||||
otel.SetTextMapPropagator(prevPropagator)
|
||||
})
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
cfg config.ConfigurationTrace
|
||||
wantErr bool
|
||||
wantNil bool
|
||||
}{
|
||||
{
|
||||
name: "disabled",
|
||||
cfg: config.ConfigurationTrace{Enabled: false},
|
||||
wantNil: true,
|
||||
},
|
||||
{
|
||||
name: "http",
|
||||
cfg: config.ConfigurationTrace{Enabled: true, Protocol: "http", Endpoint: "localhost:4318"},
|
||||
},
|
||||
{
|
||||
name: "http default protocol",
|
||||
cfg: config.ConfigurationTrace{Enabled: true, Endpoint: "localhost:4318"},
|
||||
},
|
||||
{
|
||||
name: "http insecure with custom service name and ratio",
|
||||
cfg: config.ConfigurationTrace{
|
||||
Enabled: true,
|
||||
Protocol: "http",
|
||||
Endpoint: "localhost:4318",
|
||||
Insecure: true,
|
||||
SampleRatio: 0.5,
|
||||
ServiceName: "custom",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "grpc",
|
||||
cfg: config.ConfigurationTrace{Enabled: true, Protocol: "grpc", Endpoint: "localhost:4317", Insecure: true},
|
||||
},
|
||||
{
|
||||
name: "grpc exporter error",
|
||||
cfg: config.ConfigurationTrace{Enabled: true, Protocol: "grpc", Endpoint: "\x00", Insecure: true},
|
||||
wantErr: true,
|
||||
wantNil: true,
|
||||
},
|
||||
{
|
||||
name: "unknown protocol",
|
||||
cfg: config.ConfigurationTrace{Enabled: true, Protocol: "bogus", Endpoint: "localhost:4317"},
|
||||
wantErr: true,
|
||||
wantNil: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
shutdown, err := Init(&config.Configuration{Trace: tt.cfg})
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
require.Nil(t, shutdown)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
if tt.wantNil {
|
||||
require.Nil(t, shutdown)
|
||||
return
|
||||
}
|
||||
require.NotNil(t, shutdown)
|
||||
require.NoError(t, shutdown(t.Context()))
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user