// Package middleware provides HTTP middleware components for the CLI Proxy API server. // This file contains unit tests for the middleware components. package middleware import ( "errors" "net/http" "net/http/httptest" "testing" "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" ) func setupTestRouter() *gin.Engine { gin.SetMode(gin.TestMode) return gin.New() } func TestRequestIDMiddleware(t *testing.T) { router := setupTestRouter() router.Use(RequestID()) router.GET("/test", func(c *gin.Context) { id := c.GetString("request_id") c.JSON(200, gin.H{"request_id": id}) }) t.Run("generates request ID when not provided", func(t *testing.T) { w := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/test", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, 200, w.Code) // Check that response contains a request ID assert.Contains(t, w.Body.String(), "request_id") // Check that response header contains the request ID assert.NotEmpty(t, w.Header().Get("X-Request-ID")) }) t.Run("uses existing request ID from header", func(t *testing.T) { w := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/test", http.NoBody) req.Header.Set("X-Request-ID", "test-request-id-123") router.ServeHTTP(w, req) assert.Equal(t, 200, w.Code) assert.Contains(t, w.Body.String(), "test-request-id-123") assert.Equal(t, "test-request-id-123", w.Header().Get("X-Request-ID")) }) } func TestErrorHandler(t *testing.T) { router := setupTestRouter() router.Use(RequestID()) router.Use(ErrorHandler(logrus.New())) router.GET("/error", func(c *gin.Context) { c.Error(errors.New("test error")) c.Abort() }) router.GET("/success", func(c *gin.Context) { c.JSON(200, gin.H{"status": "ok"}) }) t.Run("handles errors gracefully", func(t *testing.T) { w := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/error", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, 500, w.Code) assert.Contains(t, w.Body.String(), "An unexpected error occurred") }) t.Run("passes through successful requests", func(t *testing.T) { w := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/success", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, 200, w.Code) assert.Contains(t, w.Body.String(), "ok") }) } func TestRecoveryMiddleware(t *testing.T) { router := setupTestRouter() router.Use(RecoveryMiddleware(nil)) router.Use(RequestID()) router.Use(ErrorHandler(logrus.New())) router.GET("/panic", func(c *gin.Context) { panic("test panic") }) router.GET("/normal", func(c *gin.Context) { c.JSON(200, gin.H{"status": "ok"}) }) t.Run("recovers from panic", func(t *testing.T) { w := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/panic", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, 500, w.Code) assert.Contains(t, w.Body.String(), "An internal server error occurred") }) t.Run("normal requests work after panic recovery", func(t *testing.T) { w := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/normal", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, 200, w.Code) assert.Contains(t, w.Body.String(), "ok") }) } func TestSafeHandler(t *testing.T) { router := setupTestRouter() router.Use(RequestID()) router.Use(ErrorHandler(logrus.New())) router.GET("/safe-panic", SafeHandler(func(c *gin.Context) { panic("safe handler panic") })) t.Run("safe handler recovers from panic", func(t *testing.T) { w := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/safe-panic", http.NoBody) router.ServeHTTP(w, req) assert.Equal(t, 500, w.Code) assert.Contains(t, w.Body.String(), "An internal server error occurred") }) }