mirror of
https://github.com/lukaszraczylo/graphql-monitoring-proxy.git
synced 2026-06-05 23:03:48 +00:00
c2c75d69c0
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.
121 lines
3.9 KiB
Go
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())
|
|
}
|