veza/veza-backend-api/internal/middleware/rbac_middleware_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

394 lines
11 KiB
Go

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")
// "invalid" est une string, donc le code essaie de la parser en UUID et échoue
// Le message d'erreur est donc "invalid user id format" et non "invalid user id type"
assert.Equal(t, "invalid user id format", 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")
// "invalid" est une string, donc le code essaie de la parser en UUID et échoue
// Le message d'erreur est donc "invalid user id format" et non "invalid user id type"
assert.Equal(t, "invalid user id format", errorObj["message"])
mockRoleService.AssertNotCalled(t, "HasRole")
}