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 TestRequestID_GeneratesUUID(t *testing.T) { gin.SetMode(gin.TestMode) router := gin.New() router.Use(RequestID()) router.GET("/test", func(c *gin.Context) { requestID, exists := c.Get("request_id") require.True(t, exists, "request_id should be set in context") c.JSON(http.StatusOK, gin.H{"request_id": requestID}) }) 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-Request-ID est présent headerValue := w.Header().Get("X-Request-ID") assert.NotEmpty(t, headerValue, "X-Request-ID header should be present") // Vérifier que c'est un UUID valide _, err := uuid.Parse(headerValue) assert.NoError(t, err, "X-Request-ID should be a valid UUID") // Vérifier que le request ID est dans la réponse JSON var response map[string]interface{} err = json.Unmarshal(w.Body.Bytes(), &response) require.NoError(t, err) responseRequestID, ok := response["request_id"].(string) require.True(t, ok, "request_id should be a string in response") assert.Equal(t, headerValue, responseRequestID, "request_id in response should match header") } func TestRequestID_UsesExistingHeader(t *testing.T) { gin.SetMode(gin.TestMode) router := gin.New() router.Use(RequestID()) customRequestID := "custom-request-id-12345" router.GET("/test", func(c *gin.Context) { requestID, exists := c.Get("request_id") require.True(t, exists, "request_id should be set in context") c.JSON(http.StatusOK, gin.H{"request_id": requestID}) }) w := httptest.NewRecorder() req := httptest.NewRequest("GET", "/test", nil) req.Header.Set("X-Request-ID", customRequestID) router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) // Vérifier que le header X-Request-ID utilise la valeur fournie headerValue := w.Header().Get("X-Request-ID") assert.Equal(t, customRequestID, headerValue, "X-Request-ID should use provided header value") // Vérifier que le request ID est dans la réponse JSON var response map[string]interface{} err := json.Unmarshal(w.Body.Bytes(), &response) require.NoError(t, err) responseRequestID, ok := response["request_id"].(string) require.True(t, ok, "request_id should be a string in response") assert.Equal(t, customRequestID, responseRequestID, "request_id in response should match provided header") } func TestRequestID_UniquePerRequest(t *testing.T) { gin.SetMode(gin.TestMode) router := gin.New() router.Use(RequestID()) router.GET("/test", func(c *gin.Context) { requestID, _ := c.Get("request_id") c.JSON(http.StatusOK, gin.H{"request_id": requestID}) }) // Faire plusieurs requêtes et vérifier qu'elles ont des IDs différents requestIDs := make(map[string]bool) for i := 0; i < 10; i++ { w := httptest.NewRecorder() req := httptest.NewRequest("GET", "/test", nil) router.ServeHTTP(w, req) var response map[string]interface{} err := json.Unmarshal(w.Body.Bytes(), &response) require.NoError(t, err) requestID, ok := response["request_id"].(string) require.True(t, ok, "request_id should be a string") // Vérifier que chaque ID est unique assert.False(t, requestIDs[requestID], "Each request should have a unique ID") requestIDs[requestID] = true } } func TestRequestID_EmptyHeaderGeneratesNew(t *testing.T) { gin.SetMode(gin.TestMode) router := gin.New() router.Use(RequestID()) router.GET("/test", func(c *gin.Context) { requestID, _ := c.Get("request_id") c.JSON(http.StatusOK, gin.H{"request_id": requestID}) }) w := httptest.NewRecorder() req := httptest.NewRequest("GET", "/test", nil) // Ne pas définir de header X-Request-ID router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) headerValue := w.Header().Get("X-Request-ID") assert.NotEmpty(t, headerValue, "X-Request-ID should be generated even if not provided") // Vérifier que c'est un UUID valide _, err := uuid.Parse(headerValue) assert.NoError(t, err, "Generated X-Request-ID should be a valid UUID") } func TestRequestID_AvailableInLogger(t *testing.T) { gin.SetMode(gin.TestMode) router := gin.New() router.Use(RequestID()) var capturedRequestID string router.GET("/test", func(c *gin.Context) { // Simuler l'utilisation dans un logger if requestID, exists := c.Get("request_id"); exists { capturedRequestID = requestID.(string) } c.JSON(http.StatusOK, gin.H{"ok": true}) }) w := httptest.NewRecorder() req := httptest.NewRequest("GET", "/test", nil) router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) assert.NotEmpty(t, capturedRequestID, "request_id should be available for logger") // Vérifier que le request ID capturé correspond au header headerValue := w.Header().Get("X-Request-ID") assert.Equal(t, headerValue, capturedRequestID, "captured request_id should match header") } func TestRequestID_MultipleRequests(t *testing.T) { gin.SetMode(gin.TestMode) router := gin.New() router.Use(RequestID()) // Utiliser router.Any() pour accepter toutes les méthodes HTTP testées router.Any("/test", func(c *gin.Context) { requestID, _ := c.Get("request_id") c.JSON(http.StatusOK, gin.H{"request_id": requestID}) }) // Tester avec différentes méthodes HTTP methods := []string{"GET", "POST", "PUT", "DELETE"} for _, method := range methods { w := httptest.NewRecorder() req := httptest.NewRequest(method, "/test", nil) router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code, "Request with method %s should return 200", method) headerValue := w.Header().Get("X-Request-ID") assert.NotEmpty(t, headerValue, "X-Request-ID should be present for %s requests", method) } }