veza/veza-backend-api/internal/tracing/trace_context_test.go
senke 0ac3b82962 [BE-SVC-018] be-svc: Implement request tracing
- Created TraceContext struct for distributed tracing
- Implemented W3C Trace Context format support (traceparent header)
- Added backward compatibility with legacy X-Trace-ID and X-Span-ID headers
- Created HTTPClientWithTracing for automatic trace propagation in outgoing requests
- Enhanced Tracing middleware to use new trace context system
- Added context propagation helpers (WithTraceContext, FromContext)
- Added child span creation for nested operations
- Comprehensive unit tests for trace context and HTTP client

Phase: PHASE-6
Priority: P2
Progress: 114/267 (42.70%)
2025-12-24 17:05:32 +01:00

137 lines
4 KiB
Go

package tracing
import (
"context"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNewTraceContext(t *testing.T) {
tc := NewTraceContext()
assert.NotEmpty(t, tc.TraceID)
assert.NotEmpty(t, tc.SpanID)
assert.True(t, tc.Sampled)
assert.Empty(t, tc.ParentSpan)
}
func TestNewChildSpan(t *testing.T) {
parent := NewTraceContext()
child := parent.NewChildSpan()
assert.Equal(t, parent.TraceID, child.TraceID, "Child should have same trace ID")
assert.NotEqual(t, parent.SpanID, child.SpanID, "Child should have different span ID")
assert.Equal(t, parent.SpanID, child.ParentSpan, "Child parent should be parent's span ID")
assert.Equal(t, parent.Sampled, child.Sampled, "Child should inherit sampled flag")
}
func TestToW3CTraceParent(t *testing.T) {
tc := NewTraceContext()
traceParent := tc.ToW3CTraceParent()
// Format: version-trace_id-parent_id-flags
// Version: 00
// Trace ID: 32 hex chars
// Parent ID: 16 hex chars (0000000000000000 si pas de parent)
// Flags: 01 (sampled) ou 00 (not sampled)
assert.Contains(t, traceParent, "00-", "Should start with version 00")
assert.Len(t, traceParent, 55, "W3C traceparent should be 55 characters") // 00-32-16-2
}
func TestFromW3CTraceParent(t *testing.T) {
// Format valide: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
traceParent := "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
tc, err := FromW3CTraceParent(traceParent)
require.NoError(t, err)
assert.Equal(t, "4bf92f3577b34da6a3ce929d0e0e4736", tc.TraceID)
assert.Equal(t, "00f067aa0ba902b7", tc.ParentSpan)
assert.True(t, tc.Sampled)
assert.NotEmpty(t, tc.SpanID) // Nouveau span ID généré
}
func TestFromW3CTraceParent_InvalidFormat(t *testing.T) {
_, err := FromW3CTraceParent("invalid")
assert.Error(t, err)
}
func TestFromW3CTraceParent_UnsupportedVersion(t *testing.T) {
_, err := FromW3CTraceParent("01-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01")
assert.Error(t, err)
assert.Contains(t, err.Error(), "unsupported")
}
func TestExtractTraceContext_FromW3C(t *testing.T) {
req, _ := http.NewRequest("GET", "http://example.com", nil)
req.Header.Set(TraceParentHeader, "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01")
tc := ExtractTraceContext(req)
assert.NotNil(t, tc)
assert.Equal(t, "4bf92f3577b34da6a3ce929d0e0e4736", tc.TraceID)
}
func TestExtractTraceContext_FromLegacy(t *testing.T) {
req, _ := http.NewRequest("GET", "http://example.com", nil)
req.Header.Set(LegacyTraceIDHeader, "test-trace-id")
req.Header.Set(LegacySpanIDHeader, "test-span-id")
tc := ExtractTraceContext(req)
assert.NotNil(t, tc)
assert.Equal(t, "test-trace-id", tc.TraceID)
assert.Equal(t, "test-span-id", tc.SpanID)
}
func TestExtractTraceContext_New(t *testing.T) {
req, _ := http.NewRequest("GET", "http://example.com", nil)
tc := ExtractTraceContext(req)
assert.NotNil(t, tc)
assert.NotEmpty(t, tc.TraceID)
assert.NotEmpty(t, tc.SpanID)
}
func TestInjectTraceContext(t *testing.T) {
req, _ := http.NewRequest("GET", "http://example.com", nil)
tc := NewTraceContext()
InjectTraceContext(req, tc)
assert.Equal(t, tc.ToW3CTraceParent(), req.Header.Get(TraceParentHeader))
assert.Equal(t, tc.TraceID, req.Header.Get(LegacyTraceIDHeader))
assert.Equal(t, tc.SpanID, req.Header.Get(LegacySpanIDHeader))
}
func TestWithTraceContext(t *testing.T) {
ctx := context.Background()
tc := NewTraceContext()
ctx = WithTraceContext(ctx, tc)
extracted := FromContext(ctx)
assert.Equal(t, tc, extracted)
}
func TestFromContext_NotFound(t *testing.T) {
ctx := context.Background()
tc := FromContext(ctx)
assert.Nil(t, tc)
}
func TestGetOrCreateTraceContext_Existing(t *testing.T) {
ctx := context.Background()
tc := NewTraceContext()
ctx = WithTraceContext(ctx, tc)
result := GetOrCreateTraceContext(ctx)
assert.Equal(t, tc, result)
}
func TestGetOrCreateTraceContext_New(t *testing.T) {
ctx := context.Background()
result := GetOrCreateTraceContext(ctx)
assert.NotNil(t, result)
assert.NotEmpty(t, result.TraceID)
}