251 lines
7 KiB
Go
251 lines
7 KiB
Go
package middleware
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestTracing_GeneratesTraceID(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
router := gin.New()
|
|
router.Use(Tracing())
|
|
router.GET("/test", func(c *gin.Context) {
|
|
traceID, exists := c.Get(TraceIDKey)
|
|
require.True(t, exists)
|
|
c.JSON(200, gin.H{"trace_id": traceID})
|
|
})
|
|
|
|
w := httptest.NewRecorder()
|
|
req := httptest.NewRequest("GET", "/test", nil)
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
|
|
// Vérifier que le header X-Trace-ID est présent dans la réponse
|
|
traceIDHeader := w.Header().Get(TraceIDHeader)
|
|
assert.NotEmpty(t, traceIDHeader, "X-Trace-ID header should be present")
|
|
|
|
// Vérifier que c'est un UUID valide
|
|
_, err := uuid.Parse(traceIDHeader)
|
|
assert.NoError(t, err, "Trace ID should be a valid UUID")
|
|
|
|
// Vérifier dans la réponse JSON
|
|
var response map[string]interface{}
|
|
err = json.Unmarshal(w.Body.Bytes(), &response)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, traceIDHeader, response["trace_id"])
|
|
}
|
|
|
|
func TestTracing_PropagatesTraceID(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
router := gin.New()
|
|
router.Use(Tracing())
|
|
router.GET("/test", func(c *gin.Context) {
|
|
traceID := GetTraceID(c)
|
|
c.JSON(200, gin.H{"trace_id": traceID})
|
|
})
|
|
|
|
// Générer un trace ID existant
|
|
existingTraceID := uuid.New().String()
|
|
|
|
w := httptest.NewRecorder()
|
|
req := httptest.NewRequest("GET", "/test", nil)
|
|
req.Header.Set(TraceIDHeader, existingTraceID)
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
|
|
// Vérifier que le trace ID propagé est réutilisé
|
|
traceIDHeader := w.Header().Get(TraceIDHeader)
|
|
assert.Equal(t, existingTraceID, traceIDHeader, "Trace ID should be propagated")
|
|
|
|
var response map[string]interface{}
|
|
err := json.Unmarshal(w.Body.Bytes(), &response)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, existingTraceID, response["trace_id"])
|
|
}
|
|
|
|
func TestTracing_GeneratesSpanID(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
router := gin.New()
|
|
router.Use(Tracing())
|
|
router.GET("/test", func(c *gin.Context) {
|
|
spanID := GetSpanID(c)
|
|
c.JSON(200, gin.H{"span_id": spanID})
|
|
})
|
|
|
|
w := httptest.NewRecorder()
|
|
req := httptest.NewRequest("GET", "/test", nil)
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
|
|
// Vérifier que le header X-Span-ID est présent
|
|
spanIDHeader := w.Header().Get(SpanIDHeader)
|
|
assert.NotEmpty(t, spanIDHeader, "X-Span-ID header should be present")
|
|
|
|
// Vérifier que c'est un UUID valide
|
|
_, err := uuid.Parse(spanIDHeader)
|
|
assert.NoError(t, err, "Span ID should be a valid UUID")
|
|
|
|
var response map[string]interface{}
|
|
err = json.Unmarshal(w.Body.Bytes(), &response)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, spanIDHeader, response["span_id"])
|
|
}
|
|
|
|
func TestTracing_PropagatesSpanID(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
router := gin.New()
|
|
router.Use(Tracing())
|
|
router.GET("/test", func(c *gin.Context) {
|
|
spanID := GetSpanID(c)
|
|
c.JSON(200, gin.H{"span_id": spanID})
|
|
})
|
|
|
|
// Générer un span ID existant
|
|
existingSpanID := uuid.New().String()
|
|
|
|
w := httptest.NewRecorder()
|
|
req := httptest.NewRequest("GET", "/test", nil)
|
|
req.Header.Set(SpanIDHeader, existingSpanID)
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
|
|
// Vérifier que le span ID propagé est réutilisé
|
|
spanIDHeader := w.Header().Get(SpanIDHeader)
|
|
assert.Equal(t, existingSpanID, spanIDHeader, "Span ID should be propagated")
|
|
|
|
var response map[string]interface{}
|
|
err := json.Unmarshal(w.Body.Bytes(), &response)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, existingSpanID, response["span_id"])
|
|
}
|
|
|
|
func TestTracing_UniqueTraceIDs(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
router := gin.New()
|
|
router.Use(Tracing())
|
|
router.GET("/test", func(c *gin.Context) {
|
|
traceID := GetTraceID(c)
|
|
c.JSON(200, gin.H{"trace_id": traceID})
|
|
})
|
|
|
|
// Générer plusieurs requêtes et vérifier que chaque trace ID est unique
|
|
traceIDs := make(map[string]bool)
|
|
for i := 0; i < 10; i++ {
|
|
w := httptest.NewRecorder()
|
|
req := httptest.NewRequest("GET", "/test", nil)
|
|
router.ServeHTTP(w, req)
|
|
|
|
traceIDHeader := w.Header().Get(TraceIDHeader)
|
|
assert.False(t, traceIDs[traceIDHeader], "Trace ID should be unique")
|
|
traceIDs[traceIDHeader] = true
|
|
}
|
|
|
|
assert.Equal(t, 10, len(traceIDs), "Should have 10 unique trace IDs")
|
|
}
|
|
|
|
func TestTracing_ContextKeys(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
router := gin.New()
|
|
router.Use(Tracing())
|
|
router.GET("/test", func(c *gin.Context) {
|
|
traceID, traceExists := c.Get(TraceIDKey)
|
|
spanID, spanExists := c.Get(SpanIDKey)
|
|
|
|
assert.True(t, traceExists, "Trace ID should be in context")
|
|
assert.True(t, spanExists, "Span ID should be in context")
|
|
assert.NotEmpty(t, traceID)
|
|
assert.NotEmpty(t, spanID)
|
|
|
|
c.JSON(200, gin.H{
|
|
"trace_id": traceID,
|
|
"span_id": spanID,
|
|
})
|
|
})
|
|
|
|
w := httptest.NewRecorder()
|
|
req := httptest.NewRequest("GET", "/test", nil)
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
}
|
|
|
|
func TestGetTraceID(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
router := gin.New()
|
|
router.Use(Tracing())
|
|
router.GET("/test", func(c *gin.Context) {
|
|
traceID := GetTraceID(c)
|
|
assert.NotEmpty(t, traceID)
|
|
|
|
// Tester avec un contexte vide (devrait retourner chaîne vide)
|
|
emptyCtx := &gin.Context{}
|
|
emptyTraceID := GetTraceID(emptyCtx)
|
|
assert.Empty(t, emptyTraceID)
|
|
|
|
c.JSON(200, gin.H{"trace_id": traceID})
|
|
})
|
|
|
|
w := httptest.NewRecorder()
|
|
req := httptest.NewRequest("GET", "/test", nil)
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
}
|
|
|
|
func TestGetSpanID(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
router := gin.New()
|
|
router.Use(Tracing())
|
|
router.GET("/test", func(c *gin.Context) {
|
|
spanID := GetSpanID(c)
|
|
assert.NotEmpty(t, spanID)
|
|
|
|
// Tester avec un contexte vide (devrait retourner chaîne vide)
|
|
emptyCtx := &gin.Context{}
|
|
emptySpanID := GetSpanID(emptyCtx)
|
|
assert.Empty(t, emptySpanID)
|
|
|
|
c.JSON(200, gin.H{"span_id": spanID})
|
|
})
|
|
|
|
w := httptest.NewRecorder()
|
|
req := httptest.NewRequest("GET", "/test", nil)
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
}
|
|
|
|
func TestTracing_W3CTraceContextCompatible(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
router := gin.New()
|
|
router.Use(Tracing())
|
|
router.GET("/test", func(c *gin.Context) {
|
|
traceID := GetTraceID(c)
|
|
c.JSON(200, gin.H{"trace_id": traceID})
|
|
})
|
|
|
|
// Tester avec un trace ID W3C Trace Context format (16 hex digits)
|
|
// Le format W3C permet traceparent: 00-{trace_id}-{span_id}-01
|
|
// Ici on teste juste que notre UUID est compatible (peut être utilisé dans traceparent)
|
|
w3cTraceID := "4bf92f3577b34da6a3ce929d0e0e4736" // 32 hex digits (128 bits, comme UUID)
|
|
|
|
w := httptest.NewRecorder()
|
|
req := httptest.NewRequest("GET", "/test", nil)
|
|
req.Header.Set(TraceIDHeader, w3cTraceID)
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
traceIDHeader := w.Header().Get(TraceIDHeader)
|
|
assert.Equal(t, w3cTraceID, traceIDHeader, "Should accept W3C-compatible trace ID")
|
|
}
|