package middleware import ( "context" "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/mock" "github.com/stretchr/testify/require" ) // MockRoleService est un mock du RoleService pour les tests RBAC // ImplĂ©mente l'interface RoleChecker pour ĂȘtre compatible avec RequireRole type MockRoleService struct { mock.Mock } func (m *MockRoleService) HasRole(ctx context.Context, userID uuid.UUID, roleName string) (bool, error) { args := m.Called(ctx, userID, roleName) return args.Bool(0), args.Error(1) } func (m *MockRoleService) HasPermission(ctx context.Context, userID uuid.UUID, resource, action string) (bool, error) { args := m.Called(ctx, userID, resource, action) return args.Bool(0), args.Error(1) } func TestRequireRole_WithValidRole(t *testing.T) { gin.SetMode(gin.TestMode) mockRoleService := new(MockRoleService) userID := uuid.New() mockRoleService.On("HasRole", mock.Anything, userID, "admin").Return(true, nil) router := gin.New() router.Use(func(c *gin.Context) { c.Set("user_id", userID) c.Next() }) router.Use(RequireRole(mockRoleService, "admin")) router.GET("/test", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"message": "success"}) }) req := httptest.NewRequest(http.MethodGet, "/test", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) mockRoleService.AssertExpectations(t) } func TestRequireRole_WithInvalidRole(t *testing.T) { gin.SetMode(gin.TestMode) mockRoleService := new(MockRoleService) userID := uuid.New() mockRoleService.On("HasRole", mock.Anything, userID, "admin").Return(false, nil) handlerCalled := false router := gin.New() router.Use(func(c *gin.Context) { c.Set("user_id", userID) c.Next() }) router.Use(RequireRole(mockRoleService, "admin")) router.GET("/test", func(c *gin.Context) { handlerCalled = true c.JSON(http.StatusOK, gin.H{"message": "success"}) }) req := httptest.NewRequest(http.MethodGet, "/test", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) assert.Equal(t, http.StatusForbidden, w.Code) assert.False(t, handlerCalled, "Handler should not be called") var response map[string]interface{} err := json.Unmarshal(w.Body.Bytes(), &response) assert.NoError(t, err) // P0: Nouveau format AppError errorObj, ok := response["error"].(map[string]interface{}) require.True(t, ok, "Error should be a map") assert.Equal(t, "insufficient permissions", errorObj["message"]) mockRoleService.AssertExpectations(t) } func TestRequireRole_WithoutUserID(t *testing.T) { gin.SetMode(gin.TestMode) mockRoleService := new(MockRoleService) handlerCalled := false router := gin.New() router.Use(RequireRole(mockRoleService, "admin")) router.GET("/test", func(c *gin.Context) { handlerCalled = true c.JSON(http.StatusOK, gin.H{"message": "success"}) }) req := httptest.NewRequest(http.MethodGet, "/test", nil) w := httptest.NewRecorder() // user_id not set router.ServeHTTP(w, req) assert.Equal(t, http.StatusUnauthorized, w.Code) assert.False(t, handlerCalled, "Handler should not be called") var response map[string]interface{} err := json.Unmarshal(w.Body.Bytes(), &response) assert.NoError(t, err) // P0: Nouveau format AppError errorObj, ok := response["error"].(map[string]interface{}) require.True(t, ok, "Error should be a map") assert.Equal(t, "unauthorized", errorObj["message"]) mockRoleService.AssertNotCalled(t, "HasRole") } func TestRequireRole_WithServiceError(t *testing.T) { gin.SetMode(gin.TestMode) mockRoleService := new(MockRoleService) userID := uuid.New() mockRoleService.On("HasRole", mock.Anything, userID, "admin").Return(false, assert.AnError) handlerCalled := false router := gin.New() router.Use(func(c *gin.Context) { c.Set("user_id", userID) c.Next() }) router.Use(RequireRole(mockRoleService, "admin")) router.GET("/test", func(c *gin.Context) { handlerCalled = true c.JSON(http.StatusOK, gin.H{"message": "success"}) }) req := httptest.NewRequest(http.MethodGet, "/test", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) assert.Equal(t, http.StatusInternalServerError, w.Code) assert.False(t, handlerCalled, "Handler should not be called") var response map[string]interface{} err := json.Unmarshal(w.Body.Bytes(), &response) assert.NoError(t, err) assert.Contains(t, response, "error") mockRoleService.AssertExpectations(t) } func TestRequireRole_WithIntUserIDType(t *testing.T) { gin.SetMode(gin.TestMode) mockRoleService := new(MockRoleService) // No expectations - should fail before calling service router := gin.New() router.Use(func(c *gin.Context) { c.Set("user_id", 123) // int instead of uuid.UUID c.Next() }) router.Use(RequireRole(mockRoleService, "admin")) router.GET("/test", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"message": "success"}) }) req := httptest.NewRequest(http.MethodGet, "/test", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) assert.Equal(t, http.StatusUnauthorized, w.Code) mockRoleService.AssertNotCalled(t, "HasRole") } func TestRequirePermission_WithValidPermission(t *testing.T) { gin.SetMode(gin.TestMode) mockRoleService := new(MockRoleService) userID := uuid.New() mockRoleService.On("HasPermission", mock.Anything, userID, "tracks", "create").Return(true, nil) router := gin.New() router.Use(func(c *gin.Context) { c.Set("user_id", userID) c.Next() }) router.Use(RequirePermission(mockRoleService, "tracks", "create")) router.POST("/test", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"message": "success"}) }) req := httptest.NewRequest(http.MethodPost, "/test", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) mockRoleService.AssertExpectations(t) } func TestRequirePermission_WithInvalidPermission(t *testing.T) { gin.SetMode(gin.TestMode) mockRoleService := new(MockRoleService) userID := uuid.New() mockRoleService.On("HasPermission", mock.Anything, userID, "tracks", "delete").Return(false, nil) handlerCalled := false router := gin.New() router.Use(func(c *gin.Context) { c.Set("user_id", userID) c.Next() }) router.Use(RequirePermission(mockRoleService, "tracks", "delete")) router.DELETE("/test", func(c *gin.Context) { handlerCalled = true c.JSON(http.StatusOK, gin.H{"message": "success"}) }) req := httptest.NewRequest(http.MethodDelete, "/test", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) assert.Equal(t, http.StatusForbidden, w.Code) assert.False(t, handlerCalled, "Handler should not be called") var response map[string]interface{} err := json.Unmarshal(w.Body.Bytes(), &response) assert.NoError(t, err) // P0: Nouveau format AppError errorObj, ok := response["error"].(map[string]interface{}) require.True(t, ok, "Error should be a map") assert.Equal(t, "insufficient permissions", errorObj["message"]) mockRoleService.AssertExpectations(t) } func TestRequirePermission_WithoutUserID(t *testing.T) { gin.SetMode(gin.TestMode) mockRoleService := new(MockRoleService) handlerCalled := false router := gin.New() router.Use(RequirePermission(mockRoleService, "tracks", "create")) router.POST("/test", func(c *gin.Context) { handlerCalled = true c.JSON(http.StatusOK, gin.H{"message": "success"}) }) req := httptest.NewRequest(http.MethodPost, "/test", nil) w := httptest.NewRecorder() // user_id not set router.ServeHTTP(w, req) assert.Equal(t, http.StatusUnauthorized, w.Code) assert.False(t, handlerCalled, "Handler should not be called") var response map[string]interface{} err := json.Unmarshal(w.Body.Bytes(), &response) assert.NoError(t, err) // P0: Nouveau format AppError errorObj, ok := response["error"].(map[string]interface{}) require.True(t, ok, "Error should be a map") assert.Equal(t, "unauthorized", errorObj["message"]) mockRoleService.AssertNotCalled(t, "HasPermission") } func TestRequirePermission_WithServiceError(t *testing.T) { gin.SetMode(gin.TestMode) mockRoleService := new(MockRoleService) userID := uuid.New() mockRoleService.On("HasPermission", mock.Anything, userID, "tracks", "create").Return(false, assert.AnError) handlerCalled := false router := gin.New() router.Use(func(c *gin.Context) { c.Set("user_id", userID) c.Next() }) router.Use(RequirePermission(mockRoleService, "tracks", "create")) router.POST("/test", func(c *gin.Context) { handlerCalled = true c.JSON(http.StatusOK, gin.H{"message": "success"}) }) req := httptest.NewRequest(http.MethodPost, "/test", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) assert.Equal(t, http.StatusInternalServerError, w.Code) assert.False(t, handlerCalled, "Handler should not be called") var response map[string]interface{} err := json.Unmarshal(w.Body.Bytes(), &response) assert.NoError(t, err) assert.Contains(t, response, "error") mockRoleService.AssertExpectations(t) } func TestRequirePermission_WithInvalidUserIDType(t *testing.T) { gin.SetMode(gin.TestMode) mockRoleService := new(MockRoleService) handlerCalled := false router := gin.New() router.Use(func(c *gin.Context) { c.Set("user_id", "invalid") // Invalid type c.Next() }) router.Use(RequirePermission(mockRoleService, "tracks", "create")) router.POST("/test", func(c *gin.Context) { handlerCalled = true c.JSON(http.StatusOK, gin.H{"message": "success"}) }) req := httptest.NewRequest(http.MethodPost, "/test", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) assert.Equal(t, http.StatusUnauthorized, w.Code) assert.False(t, handlerCalled, "Handler should not be called") var response map[string]interface{} err := json.Unmarshal(w.Body.Bytes(), &response) assert.NoError(t, err) assert.Contains(t, response, "error") // P0: Nouveau format AppError errorObj, ok := response["error"].(map[string]interface{}) require.True(t, ok, "Error should be a map") assert.Equal(t, "invalid user id type", errorObj["message"]) mockRoleService.AssertNotCalled(t, "HasPermission") } func TestRequireRole_WithInvalidUserIDType(t *testing.T) { gin.SetMode(gin.TestMode) mockRoleService := new(MockRoleService) handlerCalled := false router := gin.New() router.Use(func(c *gin.Context) { c.Set("user_id", "invalid") // Invalid type c.Next() }) router.Use(RequireRole(mockRoleService, "admin")) router.GET("/test", func(c *gin.Context) { handlerCalled = true c.JSON(http.StatusOK, gin.H{"message": "success"}) }) req := httptest.NewRequest(http.MethodGet, "/test", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) assert.Equal(t, http.StatusUnauthorized, w.Code) assert.False(t, handlerCalled, "Handler should not be called") var response map[string]interface{} err := json.Unmarshal(w.Body.Bytes(), &response) assert.NoError(t, err) assert.Contains(t, response, "error") // P0: Nouveau format AppError errorObj, ok := response["error"].(map[string]interface{}) require.True(t, ok, "Error should be a map") assert.Equal(t, "invalid user id type", errorObj["message"]) mockRoleService.AssertNotCalled(t, "HasRole") }