veza/veza-backend-api/internal/middleware/metrics_test.go

272 lines
6.9 KiB
Go
Raw Normal View History

2025-12-03 19:29:37 +00:00
package middleware
import (
"net/http/httptest"
"testing"
"time"
"github.com/gin-gonic/gin"
"github.com/prometheus/client_golang/prometheus"
dto "github.com/prometheus/client_model/go"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestMetricsMiddleware(t *testing.T) {
gin.SetMode(gin.TestMode)
router := gin.New()
router.Use(Metrics())
router.GET("/test", func(c *gin.Context) {
c.JSON(200, gin.H{"ok": true})
})
w := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/test", nil)
router.ServeHTTP(w, req)
assert.Equal(t, 200, w.Code)
// Vérifier que les métriques ont été enregistrées
// On vérifie via le registry Prometheus par défaut
registry := prometheus.DefaultRegisterer.(*prometheus.Registry)
metricFamilies, err := registry.Gather()
require.NoError(t, err)
foundRequestsTotal := false
foundDuration := false
for _, mf := range metricFamilies {
if *mf.Name == "veza_gin_http_requests_total" {
2025-12-03 19:29:37 +00:00
foundRequestsTotal = true
assert.Greater(t, len(mf.Metric), 0)
}
if *mf.Name == "veza_gin_http_request_duration_seconds" {
2025-12-03 19:29:37 +00:00
foundDuration = true
assert.Greater(t, len(mf.Metric), 0)
}
}
assert.True(t, foundRequestsTotal, "veza_gin_http_requests_total metric should exist")
assert.True(t, foundDuration, "veza_gin_http_request_duration_seconds metric should exist")
2025-12-03 19:29:37 +00:00
}
func TestMetricsMiddleware_DifferentStatusCodes(t *testing.T) {
gin.SetMode(gin.TestMode)
router := gin.New()
router.Use(Metrics())
router.GET("/ok", func(c *gin.Context) {
c.JSON(200, gin.H{"ok": true})
})
router.GET("/notfound", func(c *gin.Context) {
c.JSON(404, gin.H{"error": "not found"})
})
router.GET("/error", func(c *gin.Context) {
c.JSON(500, gin.H{"error": "internal error"})
})
// Tester différents codes de status
w1 := httptest.NewRecorder()
req1 := httptest.NewRequest("GET", "/ok", nil)
router.ServeHTTP(w1, req1)
assert.Equal(t, 200, w1.Code)
w2 := httptest.NewRecorder()
req2 := httptest.NewRequest("GET", "/notfound", nil)
router.ServeHTTP(w2, req2)
assert.Equal(t, 404, w2.Code)
w3 := httptest.NewRecorder()
req3 := httptest.NewRequest("GET", "/error", nil)
router.ServeHTTP(w3, req3)
assert.Equal(t, 500, w3.Code)
}
func TestMetricsMiddleware_DifferentMethods(t *testing.T) {
gin.SetMode(gin.TestMode)
router := gin.New()
router.Use(Metrics())
router.GET("/resource", func(c *gin.Context) {
c.JSON(200, gin.H{"method": "GET"})
})
router.POST("/resource", func(c *gin.Context) {
c.JSON(201, gin.H{"method": "POST"})
})
router.PUT("/resource", func(c *gin.Context) {
c.JSON(200, gin.H{"method": "PUT"})
})
router.DELETE("/resource", func(c *gin.Context) {
c.JSON(204, gin.H{"method": "DELETE"})
})
// Tester différentes méthodes HTTP
methods := []struct {
method string
path string
status int
}{
{"GET", "/resource", 200},
{"POST", "/resource", 201},
{"PUT", "/resource", 200},
{"DELETE", "/resource", 204},
}
for _, m := range methods {
w := httptest.NewRecorder()
req := httptest.NewRequest(m.method, m.path, nil)
router.ServeHTTP(w, req)
assert.Equal(t, m.status, w.Code)
}
}
func TestMetricsMiddleware_DurationMeasurement(t *testing.T) {
gin.SetMode(gin.TestMode)
router := gin.New()
router.Use(Metrics())
router.GET("/slow", func(c *gin.Context) {
time.Sleep(50 * time.Millisecond)
c.JSON(200, gin.H{"ok": true})
})
start := time.Now()
w := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/slow", nil)
router.ServeHTTP(w, req)
duration := time.Since(start)
assert.Equal(t, 200, w.Code)
assert.GreaterOrEqual(t, duration, 50*time.Millisecond, "Should measure at least the sleep duration")
}
func TestMetricsMiddleware_EmptyPath(t *testing.T) {
gin.SetMode(gin.TestMode)
router := gin.New()
router.Use(Metrics())
// Route sans nom de route défini
router.Any("/unknown/*path", func(c *gin.Context) {
c.JSON(200, gin.H{"ok": true})
})
w := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/unknown/test", nil)
router.ServeHTTP(w, req)
assert.Equal(t, 200, w.Code)
// Le path devrait être l'URL path si FullPath est vide
}
func TestMetricsMiddleware_MultipleRequests(t *testing.T) {
gin.SetMode(gin.TestMode)
router := gin.New()
router.Use(Metrics())
router.GET("/test", func(c *gin.Context) {
c.JSON(200, gin.H{"ok": true})
})
// Faire plusieurs requêtes
for i := 0; i < 5; i++ {
w := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/test", nil)
router.ServeHTTP(w, req)
assert.Equal(t, 200, w.Code)
}
// Vérifier que les métriques sont accumulées
registry := prometheus.DefaultRegisterer.(*prometheus.Registry)
metricFamilies, err := registry.Gather()
require.NoError(t, err)
totalRequests := 0.0
for _, mf := range metricFamilies {
if *mf.Name == "veza_gin_http_requests_total" {
2025-12-03 19:29:37 +00:00
for _, metric := range mf.Metric {
if metric.Counter != nil {
// Somme toutes les valeurs de counter pour cette métrique
totalRequests += *metric.Counter.Value
}
}
}
}
// Au moins 5 requêtes devraient être comptées au total
// (les métriques sont groupées par labels, donc on somme toutes les valeurs)
assert.GreaterOrEqual(t, totalRequests, float64(5), "Should have recorded at least 5 requests")
}
func TestMetricsMiddleware_LabelsCorrectness(t *testing.T) {
gin.SetMode(gin.TestMode)
router := gin.New()
router.Use(Metrics())
router.GET("/api/v1/users/:id", func(c *gin.Context) {
c.JSON(200, gin.H{"id": c.Param("id")})
})
w := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/api/v1/users/123", nil)
router.ServeHTTP(w, req)
assert.Equal(t, 200, w.Code)
// Vérifier que les labels sont corrects
registry := prometheus.DefaultRegisterer.(*prometheus.Registry)
metricFamilies, err := registry.Gather()
require.NoError(t, err)
for _, mf := range metricFamilies {
if *mf.Name == "veza_http_requests_total" {
for _, metric := range mf.Metric {
method := ""
path := ""
status := ""
for _, label := range metric.Label {
switch *label.Name {
case "method":
method = *label.Value
case "path":
path = *label.Value
case "status":
status = *label.Value
}
}
if method == "GET" && path == "/api/v1/users/:id" {
assert.Equal(t, "200", status)
}
}
}
}
}
func TestMetricsMiddleware_HistogramBuckets(t *testing.T) {
gin.SetMode(gin.TestMode)
router := gin.New()
router.Use(Metrics())
router.GET("/test", func(c *gin.Context) {
c.JSON(200, gin.H{"ok": true})
})
w := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/test", nil)
router.ServeHTTP(w, req)
assert.Equal(t, 200, w.Code)
// Vérifier que l'histogramme est correctement configuré
registry := prometheus.DefaultRegisterer.(*prometheus.Registry)
metricFamilies, err := registry.Gather()
require.NoError(t, err)
for _, mf := range metricFamilies {
if *mf.Name == "veza_http_request_duration_seconds" {
assert.Equal(t, dto.MetricType_HISTOGRAM, *mf.Type)
assert.Greater(t, len(mf.Metric), 0)
}
}
}