Files
graphql-monitoring-proxy/tracing/tracing_coverage_test.go
T
lukaszraczylo c2c75d69c0 perf+coverage: optimisation pass + coverage push to ≥70%
Performance / resource usage:
- circuit_breaker_metrics: fix data race on failCounters map (RWMutex + double-checked locking)
- server.go: drop user_id and op_name metric labels (Prometheus cardinality bound); de-duplicate extractUserInfo
- graphql.go: gate runtime.ReadMemStats per-request behind ENABLE_ALLOCATION_TRACKING flag (default off)
- graphql.go: collapse two-pass AST scan into single pass; lower-case once
- sanitization.go: cache compiled redaction regexes per pattern via sync.Map; hoist inner constants to pkg vars
- proxy.go: hoist connection/timeout substrings to pkg vars; sentinel errors for static error paths; drop dead Headers map alloc
- metrics_aggregator.go: log-field allocation guarded by Logger.IsLevelEnabled
- logging/logger.go: add IsLevelEnabled helper
- lru_cache.go: 16-shard sharding, FNV-1a routing (concurrent throughput +22%)
- cache/memory/lru_memory_cache.go: gzip compress/decompress moved outside mu.Lock
- rps_tracker.go: RWMutex+uint64 -> atomic.Uint64
- retry_budget.go: drop unused mutex
- api.go: bannedUsersIDs map+RWMutex -> sync.Map (+ snapshot/replace helpers)
- tracing/tracing.go: pkg-level constSpanAttrs, copy-then-append in StartSpanWithAttributes
- admin_dashboard.go: handleStatsWebSocket reuses bytes.Buffer + json.Encoder per connection

Build / runtime:
- Makefile: -ldflags="-s -w" -trimpath, CGO_ENABLED=0 for build (=1 for test recipes)
- Dockerfile + Dockerfile.goreleaser: ENV GOMEMLIMIT=512MiB
- main.go: blank import go.uber.org/automaxprocs (cgroup-aware GOMAXPROCS)
- main.go: PPROF_PORT env var wires net/http/pprof on 127.0.0.1 only with full server timeouts
- README.md: env-var docs + metric-label docs updated; cardinality note

Test coverage push (per package):
- main 51.2% -> 74.7%
- cache 66.3% -> 93.7%
- cache/redis 45.5% -> 98.2%
- tracing 66.7% -> 72.9%
- (cache/memory 91.6%, logging 91.9%, monitoring 77.6%, pkg/pools 100% unchanged)

New test files: coverage_micro_test, coverage_extras_test, server_handlers_test,
api_health_test, admin_dashboard_cluster_test, metrics_aggregator_test, concerns_test,
cache/cache_coverage_test, cache/redis/redis_coverage_test, tracing/tracing_coverage_test.

Bug fix: connection_resilience_test.go TestIntegratedHealthManagement.health_manager_startup
was sync.Once-coupled to InitializeBackendHealth and panicked when another test (e.g. via
parseConfig) had already triggered Once. Use NewBackendHealthManager directly.
2026-04-19 19:49:24 +01:00

121 lines
3.9 KiB
Go

package tracing
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/propagation"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
"go.opentelemetry.io/otel/sdk/trace/tracetest"
"go.opentelemetry.io/otel/trace/noop"
)
// TestNewTracing_NilContext covers the nil context early-return branch (line 34-36).
func TestNewTracing_NilContext_ReturnsError(t *testing.T) {
_, err := NewTracing(nil, "localhost:4317") //nolint:staticcheck // SA1012: intentional nil to test the error branch
require.Error(t, err)
assert.Contains(t, err.Error(), "context cannot be nil")
}
// TestNewTracing_InvalidEndpointFormats covers endpoint validation branches.
// Note: fmt.Sscanf("%s:%d") treats %s as greedy so any "host:port" string hits
// the format error (n!=2). The port-range branch (port>65535) requires n==2
// which Sscanf never produces for "host:port" strings — that's a source quirk.
func TestNewTracing_InvalidEndpointFormats_ReturnsError(t *testing.T) {
tests := []struct {
name string
endpoint string
}{
{name: "no port separator", endpoint: "localhost"},
{name: "port over max", endpoint: "localhost:999999"},
{name: "plain hostname only", endpoint: "myhost"},
{name: "just a number", endpoint: "12345"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
_, err := NewTracing(context.Background(), tt.endpoint)
require.Error(t, err)
assert.Contains(t, err.Error(), "invalid endpoint format")
})
}
}
// TestShutdown_WithRealProvider covers the non-nil tracerProvider shutdown path (line 133).
func TestShutdown_WithRealProvider_NoError(t *testing.T) {
// Use in-memory exporter so no network needed.
exporter := tracetest.NewInMemoryExporter()
tp := sdktrace.NewTracerProvider(
sdktrace.WithSyncer(exporter),
)
ts := &TracingSetup{
tracerProvider: tp,
tracer: tp.Tracer("shutdown-test"),
}
ctx := context.Background()
err := ts.Shutdown(ctx)
assert.NoError(t, err)
}
// TestStartSpan_WithRealTracer covers StartSpan with a real (noop) tracer — the non-nil path.
func TestStartSpan_WithRealTracer_ReturnsSpan(t *testing.T) {
tp := noop.NewTracerProvider()
ts := &TracingSetup{
tracer: tp.Tracer("start-span-test"),
}
ctx := context.Background()
span, newCtx := ts.StartSpan(ctx, "my-operation")
assert.NotNil(t, span)
assert.NotNil(t, newCtx)
span.End()
}
// TestStartSpanWithAttributes_WithRealTracer covers the non-nil tracer path with attrs.
func TestStartSpanWithAttributes_WithRealTracer_RecordsSpan(t *testing.T) {
exporter := tracetest.NewInMemoryExporter()
tp := sdktrace.NewTracerProvider(
sdktrace.WithSyncer(exporter),
sdktrace.WithSampler(sdktrace.AlwaysSample()),
)
ts := &TracingSetup{
tracerProvider: tp,
tracer: tp.Tracer("attr-test"),
}
ctx := context.Background()
attrs := map[string]string{
"user.id": "u-42",
"operation": "query",
}
span, newCtx := ts.StartSpanWithAttributes(ctx, "graphql-query", attrs)
require.NotNil(t, span)
require.NotNil(t, newCtx)
span.End()
spans := exporter.GetSpans()
require.Len(t, spans, 1)
assert.Equal(t, "graphql-query", spans[0].Name)
}
// TestExtractSpanContext_ValidTraceparent covers the valid span context branch (line 115-116).
// ExtractSpanContext uses otel.GetTextMapPropagator(); we must register the W3C
// TraceContext propagator before calling it (NewTracing normally does this).
func TestExtractSpanContext_ValidTraceparent_ReturnsValid(t *testing.T) {
otel.SetTextMapPropagator(propagation.TraceContext{})
tp := noop.NewTracerProvider()
ts := &TracingSetup{
tracer: tp.Tracer("extract-test"),
}
spanInfo := &TraceSpanInfo{
TraceParent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
}
spanCtx, err := ts.ExtractSpanContext(spanInfo)
require.NoError(t, err)
assert.True(t, spanCtx.IsValid())
}