veza/veza-backend-api/internal/middleware/tracing_test.go
senke 0e7b6fede1 [T0-002] fix(rust): Corriger erreurs compilation Rust
- Conflit SQLx résolu (alignement sur version 0.7)
- build.rs configurés pour protoc dans chat/stream servers
- API Prometheus migrée vers HistogramOpts
- Traits Display/Debug corrigés (String au lieu de &dyn Display)
- API TOTP corrigée (totp-rs 5.4 avec Secret::Encoded)
- Layers tracing-subscriber corrigés (types conditionnels)
- VezaError/VezaResult exportés dans lib.rs
- TransactionProvider simplifié (retour void au lieu de Box<dyn>)
- VezaConfig contraint Serialize pour to_json()

Files: veza-common/Cargo.toml, veza-common/src/*.rs, veza-chat-server/Cargo.toml, veza-chat-server/build.rs, veza-stream-server/Cargo.toml, veza-stream-server/build.rs, VEZA_ROADMAP.json
Hours: 8 estimated, 3 actual
2026-01-04 01:44:20 +01:00

259 lines
7.5 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)
// Ne pas créer un Context vide car Request serait nil et causerait un panic
// À la place, tester avec un contexte qui n'a pas de trace ID
w2 := httptest.NewRecorder()
emptyCtx, _ := gin.CreateTestContext(w2)
emptyCtx.Request = httptest.NewRequest("GET", "/", nil)
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)
// Ne pas créer un Context vide car Request serait nil et causerait un panic
// À la place, tester avec un contexte qui n'a pas de span ID
w2 := httptest.NewRecorder()
emptyCtx, _ := gin.CreateTestContext(w2)
emptyCtx.Request = httptest.NewRequest("GET", "/", nil)
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")
}