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

305 lines
9.8 KiB
Go

package middleware
import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"testing"
"veza-backend-api/internal/models"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)
// MockPlaylistService est un mock du PlaylistService pour les tests
type MockPlaylistService struct {
mock.Mock
}
func (m *MockPlaylistService) CheckPermission(ctx context.Context, playlistID, userID uuid.UUID, requiredPermission models.PlaylistPermission) (bool, error) {
args := m.Called(ctx, playlistID, userID, requiredPermission)
return args.Bool(0), args.Error(1)
}
// setupPlaylistPermissionTestRouter crée un router de test avec le middleware de permissions
func setupPlaylistPermissionTestRouter(t *testing.T) (*gin.Engine, *MockPlaylistService, func()) {
gin.SetMode(gin.TestMode)
// Setup mock service
mockService := new(MockPlaylistService)
// Setup router
router := gin.New()
router.Use(func(c *gin.Context) {
// Mock authentication middleware - set user_id from query param
if userIDStr := c.Query("user_id"); userIDStr != "" {
if uid, err := uuid.Parse(userIDStr); err == nil {
c.Set("user_id", uid)
}
}
c.Next()
})
// Test endpoint
router.GET("/test/:id", CheckPlaylistPermission(mockService, models.PlaylistPermissionRead), func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "success"})
})
cleanup := func() {
// Nothing to cleanup
}
return router, mockService, cleanup
}
func TestCheckPlaylistPermission_Owner(t *testing.T) {
router, mockService, cleanup := setupPlaylistPermissionTestRouter(t)
defer cleanup()
playlistID := uuid.New()
userID := uuid.New()
mockService.On("CheckPermission", mock.Anything, playlistID, userID, models.PlaylistPermissionRead).Return(true, nil)
w := httptest.NewRecorder()
req := httptest.NewRequest("GET", fmt.Sprintf("/test/%s?user_id=%s", playlistID, userID), nil)
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
var response map[string]string
json.Unmarshal(w.Body.Bytes(), &response)
assert.Equal(t, "success", response["message"])
mockService.AssertExpectations(t)
}
func TestCheckPlaylistPermission_PublicRead(t *testing.T) {
router, mockService, cleanup := setupPlaylistPermissionTestRouter(t)
defer cleanup()
playlistID := uuid.New()
userID := uuid.New()
mockService.On("CheckPermission", mock.Anything, playlistID, userID, models.PlaylistPermissionRead).Return(true, nil)
w := httptest.NewRecorder()
req := httptest.NewRequest("GET", fmt.Sprintf("/test/%s?user_id=%s", playlistID, userID), nil)
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
var response map[string]string
json.Unmarshal(w.Body.Bytes(), &response)
assert.Equal(t, "success", response["message"])
mockService.AssertExpectations(t)
}
func TestCheckPlaylistPermission_PrivateForbidden(t *testing.T) {
router, mockService, cleanup := setupPlaylistPermissionTestRouter(t)
defer cleanup()
playlistID := uuid.New()
userID := uuid.New()
mockService.On("CheckPermission", mock.Anything, playlistID, userID, models.PlaylistPermissionRead).Return(false, nil)
w := httptest.NewRecorder()
req := httptest.NewRequest("GET", fmt.Sprintf("/test/%s?user_id=%s", playlistID, userID), nil)
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusForbidden, w.Code)
var response map[string]interface{}
err := json.Unmarshal(w.Body.Bytes(), &response)
require.NoError(t, err)
// P0: Nouveau format AppError
errorObj, ok := response["error"].(map[string]interface{})
require.True(t, ok, "Error should be a map")
assert.Contains(t, errorObj["message"].(string), "forbidden")
mockService.AssertExpectations(t)
}
func TestCheckPlaylistPermission_CollaboratorRead(t *testing.T) {
router, mockService, cleanup := setupPlaylistPermissionTestRouter(t)
defer cleanup()
playlistID := uuid.New()
userID := uuid.New()
mockService.On("CheckPermission", mock.Anything, playlistID, userID, models.PlaylistPermissionRead).Return(true, nil)
w := httptest.NewRecorder()
req := httptest.NewRequest("GET", fmt.Sprintf("/test/%s?user_id=%s", playlistID, userID), nil)
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
var response map[string]string
json.Unmarshal(w.Body.Bytes(), &response)
assert.Equal(t, "success", response["message"])
mockService.AssertExpectations(t)
}
func TestCheckPlaylistPermission_CollaboratorWrite(t *testing.T) {
gin.SetMode(gin.TestMode)
mockService := new(MockPlaylistService)
playlistID := uuid.New()
userID := uuid.New()
mockService.On("CheckPermission", mock.Anything, playlistID, userID, models.PlaylistPermissionWrite).Return(true, nil)
routerWrite := gin.New()
routerWrite.Use(func(c *gin.Context) {
if userIDStr := c.Query("user_id"); userIDStr != "" {
if uid, err := uuid.Parse(userIDStr); err == nil {
c.Set("user_id", uid)
}
}
c.Next()
})
routerWrite.GET("/test/:id", RequirePlaylistWrite(mockService), func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "success"})
})
w := httptest.NewRecorder()
req := httptest.NewRequest("GET", fmt.Sprintf("/test/%s?user_id=%s", playlistID, userID), nil)
routerWrite.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
mockService.AssertExpectations(t)
}
func TestCheckPlaylistPermission_CollaboratorReadCannotWrite(t *testing.T) {
gin.SetMode(gin.TestMode)
mockService := new(MockPlaylistService)
playlistID := uuid.New()
userID := uuid.New()
mockService.On("CheckPermission", mock.Anything, playlistID, userID, models.PlaylistPermissionWrite).Return(false, nil)
routerWrite := gin.New()
routerWrite.Use(func(c *gin.Context) {
if userIDStr := c.Query("user_id"); userIDStr != "" {
if uid, err := uuid.Parse(userIDStr); err == nil {
c.Set("user_id", uid)
}
}
c.Next()
})
routerWrite.GET("/test/:id", RequirePlaylistWrite(mockService), func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "success"})
})
w := httptest.NewRecorder()
req := httptest.NewRequest("GET", fmt.Sprintf("/test/%s?user_id=%s", playlistID, userID), nil)
routerWrite.ServeHTTP(w, req)
assert.Equal(t, http.StatusForbidden, w.Code)
mockService.AssertExpectations(t)
}
func TestCheckPlaylistPermission_NotFound(t *testing.T) {
router, mockService, cleanup := setupPlaylistPermissionTestRouter(t)
defer cleanup()
playlistID := uuid.New()
userID := uuid.New()
mockService.On("CheckPermission", mock.Anything, playlistID, userID, models.PlaylistPermissionRead).Return(false, fmt.Errorf("playlist not found"))
w := httptest.NewRecorder()
req := httptest.NewRequest("GET", fmt.Sprintf("/test/%s?user_id=%s", playlistID, userID), nil)
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusNotFound, w.Code)
var response map[string]interface{}
err := json.Unmarshal(w.Body.Bytes(), &response)
require.NoError(t, err)
// P0: Nouveau format AppError
errorObj, ok := response["error"].(map[string]interface{})
require.True(t, ok, "Error should be a map")
assert.Contains(t, errorObj["message"].(string), "playlist not found")
mockService.AssertExpectations(t)
}
func TestCheckPlaylistPermission_Unauthorized(t *testing.T) {
router, mockService, cleanup := setupPlaylistPermissionTestRouter(t)
defer cleanup()
playlistID := uuid.New()
w := httptest.NewRecorder()
req := httptest.NewRequest("GET", fmt.Sprintf("/test/%s", playlistID), nil) // Pas de user_id
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusUnauthorized, w.Code)
var response map[string]interface{}
err := json.Unmarshal(w.Body.Bytes(), &response)
require.NoError(t, err)
// P0: Nouveau format AppError
errorObj, ok := response["error"].(map[string]interface{})
require.True(t, ok, "Error should be a map")
assert.Contains(t, errorObj["message"].(string), "unauthorized")
mockService.AssertNotCalled(t, "CheckPermission")
}
func TestCheckPlaylistPermission_InvalidPlaylistID(t *testing.T) {
router, mockService, cleanup := setupPlaylistPermissionTestRouter(t)
defer cleanup()
userID := uuid.New()
w := httptest.NewRecorder()
req := httptest.NewRequest("GET", fmt.Sprintf("/test/invalid?user_id=%s", userID), nil)
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusBadRequest, w.Code)
var response map[string]interface{}
err := json.Unmarshal(w.Body.Bytes(), &response)
require.NoError(t, err)
// P0: Nouveau format AppError
errorObj, ok := response["error"].(map[string]interface{})
require.True(t, ok, "Error should be a map")
assert.Contains(t, errorObj["message"].(string), "invalid playlist id")
mockService.AssertNotCalled(t, "CheckPermission")
}
func TestRequirePlaylistOwner(t *testing.T) {
gin.SetMode(gin.TestMode)
mockService := new(MockPlaylistService)
playlistID := uuid.New()
ownerID := uuid.New()
otherID := uuid.New()
mockService.On("CheckPermission", mock.Anything, playlistID, ownerID, models.PlaylistPermissionAdmin).Return(true, nil)
mockService.On("CheckPermission", mock.Anything, playlistID, otherID, models.PlaylistPermissionAdmin).Return(false, nil)
routerOwner := gin.New()
routerOwner.Use(func(c *gin.Context) {
if userIDStr := c.Query("user_id"); userIDStr != "" {
if uid, err := uuid.Parse(userIDStr); err == nil {
c.Set("user_id", uid)
}
}
c.Next()
})
routerOwner.GET("/test/:id", RequirePlaylistOwner(mockService), func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "success"})
})
// Owner peut accéder
w := httptest.NewRecorder()
req := httptest.NewRequest("GET", fmt.Sprintf("/test/%s?user_id=%s", playlistID, ownerID), nil)
routerOwner.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
// Autre utilisateur ne peut pas accéder
w2 := httptest.NewRecorder()
req2 := httptest.NewRequest("GET", fmt.Sprintf("/test/%s?user_id=%s", playlistID, otherID), nil)
routerOwner.ServeHTTP(w2, req2)
assert.Equal(t, http.StatusForbidden, w2.Code)
mockService.AssertExpectations(t)
}