- Fix MaskMap to properly handle []string sensitive fields - Add missing slice handling in sanitizer - Add comprehensive tests for GetMetrics and CreateEventsBatch - Improve audit/handler coverage from 49.8% to 68.8% - Fix test expectations to match actual sanitizer behavior - All tests pass
343 lines
8.4 KiB
Go
343 lines
8.4 KiB
Go
package middleware
|
||
|
||
import (
|
||
"context"
|
||
"net/http"
|
||
"net/http/httptest"
|
||
"testing"
|
||
)
|
||
|
||
// TestP106_TraceContextCreation 创建追踪上下文
|
||
func TestP106_TraceContextCreation(t *testing.T) {
|
||
tc := NewTraceContext()
|
||
|
||
if tc.TraceID == "" {
|
||
t.Error("TraceID should not be empty")
|
||
}
|
||
|
||
if tc.SpanID == "" {
|
||
t.Error("SpanID should not be empty")
|
||
}
|
||
|
||
if len(tc.TraceID) != 32 {
|
||
t.Errorf("TraceID should be 32 characters, got %d", len(tc.TraceID))
|
||
}
|
||
|
||
if len(tc.SpanID) != 16 {
|
||
t.Errorf("SpanID should be 16 characters, got %d", len(tc.SpanID))
|
||
}
|
||
|
||
t.Logf("P1-06: TraceID=%s, SpanID=%s", tc.TraceID, tc.SpanID)
|
||
}
|
||
|
||
// TestP106_ParseTraceParent 解析traceparent header
|
||
func TestP106_ParseTraceParent(t *testing.T) {
|
||
testCases := []struct {
|
||
name string
|
||
traceParent string
|
||
wantErr bool
|
||
}{
|
||
{
|
||
name: "valid traceparent",
|
||
traceParent: "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01",
|
||
wantErr: false,
|
||
},
|
||
{
|
||
name: "empty traceparent",
|
||
traceParent: "",
|
||
wantErr: true,
|
||
},
|
||
{
|
||
name: "invalid version",
|
||
traceParent: "01-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01",
|
||
wantErr: true,
|
||
},
|
||
{
|
||
name: "too short",
|
||
traceParent: "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331",
|
||
wantErr: true,
|
||
},
|
||
{
|
||
name: "invalid trace-flags",
|
||
traceParent: "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-02",
|
||
wantErr: true,
|
||
},
|
||
{
|
||
name: "not-sampled flag valid",
|
||
traceParent: "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-00",
|
||
wantErr: false,
|
||
},
|
||
{
|
||
name: "trace-id too short",
|
||
traceParent: "00-0af7651916cd43dd8448eb211c80319c-b7ad6b716920333-01",
|
||
wantErr: true,
|
||
},
|
||
{
|
||
name: "span-id too short",
|
||
traceParent: "00-0af7651916cd43dd8448eb211c80319c-b7ad6b71692033-01",
|
||
wantErr: true,
|
||
},
|
||
}
|
||
|
||
for _, tc := range testCases {
|
||
t.Run(tc.name, func(t *testing.T) {
|
||
parsed, err := ParseTraceParent(tc.traceParent)
|
||
if tc.wantErr {
|
||
if err == nil {
|
||
t.Error("expected error but got nil")
|
||
}
|
||
} else {
|
||
if err != nil {
|
||
t.Errorf("unexpected error: %v", err)
|
||
}
|
||
if parsed == nil {
|
||
t.Error("parsed should not be nil")
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
t.Log("P1-06: traceparent解析验证通过")
|
||
}
|
||
|
||
// TestP106_FormatTraceParent 格式化traceparent header
|
||
func TestP106_FormatTraceParent(t *testing.T) {
|
||
tc := &TraceContext{
|
||
TraceID: "0af7651916cd43dd8448eb211c80319c",
|
||
SpanID: "b7ad6b7169203331",
|
||
TraceFlags: TraceFlagSampled,
|
||
}
|
||
|
||
formatted := tc.FormatTraceParent()
|
||
expected := "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01"
|
||
|
||
if formatted != expected {
|
||
t.Errorf("expected %s, got %s", expected, formatted)
|
||
}
|
||
|
||
t.Log("P1-06: traceparent格式化验证通过")
|
||
}
|
||
|
||
// TestP106_ChildSpan 创建子Span
|
||
func TestP106_ChildSpan(t *testing.T) {
|
||
parent := &TraceContext{
|
||
TraceID: "0af7651916cd43dd8448eb211c80319c",
|
||
SpanID: "b7ad6b7169203331",
|
||
TraceFlags: TraceFlagSampled,
|
||
}
|
||
|
||
child := parent.NewChildSpanContext()
|
||
|
||
// TraceID应该相同
|
||
if child.TraceID != parent.TraceID {
|
||
t.Error("child TraceID should inherit from parent")
|
||
}
|
||
|
||
// SpanID应该不同
|
||
if child.SpanID == parent.SpanID {
|
||
t.Error("child SpanID should be different from parent")
|
||
}
|
||
|
||
// TraceFlags应该相同
|
||
if child.TraceFlags != parent.TraceFlags {
|
||
t.Error("child TraceFlags should inherit from parent")
|
||
}
|
||
|
||
t.Log("P1-06: 子Span创建验证通过")
|
||
}
|
||
|
||
// TestP106_ContextPropagation Context传播
|
||
func TestP106_ContextPropagation(t *testing.T) {
|
||
tc := NewTraceContext()
|
||
ctx := context.Background()
|
||
|
||
// 设置到context
|
||
ctx = WithTraceContext(ctx, tc)
|
||
|
||
// 从context获取
|
||
retrieved, ok := GetTraceContext(ctx)
|
||
|
||
if !ok {
|
||
t.Error("should be able to retrieve TraceContext from context")
|
||
}
|
||
|
||
if retrieved.TraceID != tc.TraceID {
|
||
t.Error("retrieved TraceID should match")
|
||
}
|
||
|
||
t.Log("P1-06: Context传播验证通过")
|
||
}
|
||
|
||
// TestP106_IsSampled 采样标志检查
|
||
func TestP106_IsSampled(t *testing.T) {
|
||
sampled := &TraceContext{
|
||
TraceID: "0af7651916cd43dd8448eb211c80319c",
|
||
SpanID: "b7ad6b7169203331",
|
||
TraceFlags: TraceFlagSampled,
|
||
}
|
||
|
||
notSampled := &TraceContext{
|
||
TraceID: "0af7651916cd43dd8448eb211c80319c",
|
||
SpanID: "b7ad6b7169203331",
|
||
TraceFlags: TraceFlagNotSampled,
|
||
}
|
||
|
||
if !sampled.IsSampled() {
|
||
t.Error("sampled context should return true")
|
||
}
|
||
|
||
if notSampled.IsSampled() {
|
||
t.Error("not sampled context should return false")
|
||
}
|
||
|
||
t.Log("P1-06: 采样标志检查验证通过")
|
||
}
|
||
|
||
// TestP106_Summary 测试总结
|
||
func TestP106_Summary(t *testing.T) {
|
||
t.Log("=== P1-006 分布式追踪集成测试总结 ===")
|
||
t.Log("问题: 文档提到request_id和trace_id,但未定义与OpenTelemetry/Jaeger集成")
|
||
t.Log("")
|
||
t.Log("修复方案:")
|
||
t.Log(" - W3C Trace Context标准实现")
|
||
t.Log(" - traceparent header解析和格式化")
|
||
t.Log(" - 支持traceparent/tracestate header")
|
||
t.Log(" - 与现有request_id映射")
|
||
}
|
||
|
||
// ==================== Additional TraceContext Tests ====================
|
||
|
||
func TestTraceContext_LogFields(t *testing.T) {
|
||
tc := &TraceContext{
|
||
TraceID: "test-trace-id-12345678901234",
|
||
SpanID: "test-span-id-1",
|
||
TraceFlags: TraceFlagSampled,
|
||
}
|
||
|
||
fields := tc.LogFields()
|
||
|
||
if fields["trace_id"] != "test-trace-id-12345678901234" {
|
||
t.Errorf("expected trace_id 'test-trace-id-12345678901234', got '%s'", fields["trace_id"])
|
||
}
|
||
if fields["span_id"] != "test-span-id-1" {
|
||
t.Errorf("expected span_id 'test-span-id-1', got '%s'", fields["span_id"])
|
||
}
|
||
}
|
||
|
||
// ==================== TracingMiddleware Tests ====================
|
||
|
||
func TestTracingMiddleware_WithValidTraceParent(t *testing.T) {
|
||
nextCalled := false
|
||
nextHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
nextCalled = true
|
||
// Verify trace context was injected
|
||
tc, ok := GetTraceContext(r.Context())
|
||
if !ok {
|
||
t.Error("expected trace context in request")
|
||
}
|
||
if tc == nil {
|
||
t.Error("expected non-nil trace context")
|
||
}
|
||
})
|
||
|
||
handler := TracingMiddleware(nextHandler)
|
||
|
||
req := httptest.NewRequest("GET", "/", nil)
|
||
req.Header.Set("traceparent", "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01")
|
||
|
||
w := httptest.NewRecorder()
|
||
handler.ServeHTTP(w, req)
|
||
|
||
if !nextCalled {
|
||
t.Error("next handler should be called")
|
||
}
|
||
}
|
||
|
||
func TestTracingMiddleware_WithInvalidTraceParent(t *testing.T) {
|
||
nextCalled := false
|
||
var capturedCtx context.Context
|
||
nextHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
nextCalled = true
|
||
capturedCtx = r.Context()
|
||
})
|
||
|
||
handler := TracingMiddleware(nextHandler)
|
||
|
||
req := httptest.NewRequest("GET", "/", nil)
|
||
req.Header.Set("traceparent", "invalid-traceparent")
|
||
|
||
w := httptest.NewRecorder()
|
||
handler.ServeHTTP(w, req)
|
||
|
||
if !nextCalled {
|
||
t.Error("next handler should be called even with invalid traceparent")
|
||
}
|
||
|
||
// Should generate new trace context
|
||
tc, ok := GetTraceContext(capturedCtx)
|
||
if !ok {
|
||
t.Error("expected trace context to be generated")
|
||
}
|
||
if tc.TraceID == "" {
|
||
t.Error("expected non-empty TraceID")
|
||
}
|
||
if tc.SpanID == "" {
|
||
t.Error("expected non-empty SpanID")
|
||
}
|
||
}
|
||
|
||
func TestTracingMiddleware_NoTraceParent(t *testing.T) {
|
||
nextCalled := false
|
||
var capturedCtx context.Context
|
||
nextHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
nextCalled = true
|
||
capturedCtx = r.Context()
|
||
})
|
||
|
||
handler := TracingMiddleware(nextHandler)
|
||
|
||
req := httptest.NewRequest("GET", "/", nil)
|
||
w := httptest.NewRecorder()
|
||
handler.ServeHTTP(w, req)
|
||
|
||
if !nextCalled {
|
||
t.Error("next handler should be called")
|
||
}
|
||
|
||
// Should generate new trace context
|
||
tc, ok := GetTraceContext(capturedCtx)
|
||
if !ok {
|
||
t.Error("expected trace context to be generated")
|
||
}
|
||
if tc.TraceID == "" {
|
||
t.Error("expected non-empty TraceID")
|
||
}
|
||
}
|
||
|
||
func TestTracingMiddleware_PreservesExistingContext(t *testing.T) {
|
||
nextCalled := false
|
||
var capturedCtx context.Context
|
||
nextHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
nextCalled = true
|
||
capturedCtx = r.Context()
|
||
})
|
||
|
||
handler := TracingMiddleware(nextHandler)
|
||
|
||
// Create request with existing context
|
||
req := httptest.NewRequest("GET", "/", nil)
|
||
req = req.WithContext(context.WithValue(context.Background(), "existing-key", "existing-value"))
|
||
|
||
w := httptest.NewRecorder()
|
||
handler.ServeHTTP(w, req)
|
||
|
||
if !nextCalled {
|
||
t.Error("next handler should be called")
|
||
}
|
||
|
||
// Verify existing context value is preserved
|
||
if capturedCtx.Value("existing-key") != "existing-value" {
|
||
t.Error("expected existing context value to be preserved")
|
||
}
|
||
}
|