488 lines
14 KiB
Go
488 lines
14 KiB
Go
package handlers
|
|
|
|
import (
|
|
"bytes"
|
|
"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"
|
|
"go.uber.org/zap/zaptest"
|
|
|
|
"veza-backend-api/internal/services"
|
|
)
|
|
|
|
// MockRBACService is a mock implementation of RBACService for testing
|
|
type MockRBACService struct {
|
|
mock.Mock
|
|
}
|
|
|
|
func (m *MockRBACService) CreateRole(ctx context.Context, name, description string, permissions []uuid.UUID) (*services.Role, error) {
|
|
args := m.Called(ctx, name, description, permissions)
|
|
if args.Get(0) == nil {
|
|
return nil, args.Error(1)
|
|
}
|
|
return args.Get(0).(*services.Role), args.Error(1)
|
|
}
|
|
|
|
func (m *MockRBACService) GetRoleByID(ctx context.Context, roleID uuid.UUID) (*services.Role, error) {
|
|
args := m.Called(ctx, roleID)
|
|
if args.Get(0) == nil {
|
|
return nil, args.Error(1)
|
|
}
|
|
return args.Get(0).(*services.Role), args.Error(1)
|
|
}
|
|
|
|
func (m *MockRBACService) GetAllRoles(ctx context.Context) ([]*services.Role, error) {
|
|
args := m.Called(ctx)
|
|
if args.Get(0) == nil {
|
|
return nil, args.Error(1)
|
|
}
|
|
return args.Get(0).([]*services.Role), args.Error(1)
|
|
}
|
|
|
|
func (m *MockRBACService) AssignRoleToUser(ctx context.Context, userID, roleID uuid.UUID) error {
|
|
args := m.Called(ctx, userID, roleID)
|
|
return args.Error(0)
|
|
}
|
|
|
|
func (m *MockRBACService) RemoveRoleFromUser(ctx context.Context, userID, roleID uuid.UUID) error {
|
|
args := m.Called(ctx, userID, roleID)
|
|
return args.Error(0)
|
|
}
|
|
|
|
func (m *MockRBACService) GetUserRoles(ctx context.Context, userID uuid.UUID) ([]*services.Role, error) {
|
|
args := m.Called(ctx, userID)
|
|
if args.Get(0) == nil {
|
|
return nil, args.Error(1)
|
|
}
|
|
return args.Get(0).([]*services.Role), args.Error(1)
|
|
}
|
|
|
|
func (m *MockRBACService) GetUserPermissions(ctx context.Context, userID uuid.UUID) ([]services.Permission, error) {
|
|
args := m.Called(ctx, userID)
|
|
if args.Get(0) == nil {
|
|
return nil, args.Error(1)
|
|
}
|
|
return args.Get(0).([]services.Permission), args.Error(1)
|
|
}
|
|
|
|
func (m *MockRBACService) CheckPermission(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 (m *MockRBACService) CreatePermission(ctx context.Context, name, description, resource, action string) (*services.Permission, error) {
|
|
args := m.Called(ctx, name, description, resource, action)
|
|
if args.Get(0) == nil {
|
|
return nil, args.Error(1)
|
|
}
|
|
return args.Get(0).(*services.Permission), args.Error(1)
|
|
}
|
|
|
|
// setupTestRBACRouter creates a test router with RBAC handlers
|
|
func setupTestRBACRouter(mockService *MockRBACService) *gin.Engine {
|
|
gin.SetMode(gin.TestMode)
|
|
router := gin.New()
|
|
|
|
logger := zaptest.NewLogger(&testing.T{})
|
|
handler := NewRBACHandlersWithInterface(mockService, logger)
|
|
|
|
api := router.Group("/api/v1")
|
|
{
|
|
roles := api.Group("/roles")
|
|
{
|
|
roles.POST("", handler.CreateRole)
|
|
roles.GET("", handler.GetAllRoles)
|
|
roles.GET("/:id", handler.GetRole)
|
|
}
|
|
|
|
permissions := api.Group("/permissions")
|
|
{
|
|
permissions.POST("", handler.CreatePermission)
|
|
}
|
|
|
|
users := api.Group("/users")
|
|
{
|
|
users.POST("/:user_id/roles", handler.AssignRoleToUser)
|
|
users.DELETE("/:user_id/roles/:role_id", handler.RemoveRoleFromUser)
|
|
users.GET("/:user_id/roles", handler.GetUserRoles)
|
|
users.GET("/:user_id/permissions", handler.GetUserPermissions)
|
|
users.GET("/:user_id/permissions/check", handler.CheckPermission)
|
|
}
|
|
}
|
|
|
|
return router
|
|
}
|
|
|
|
func TestRBACHandlers_CreateRole_Success(t *testing.T) {
|
|
mockService := new(MockRBACService)
|
|
router := setupTestRBACRouter(mockService)
|
|
|
|
roleID := uuid.New()
|
|
expectedRole := &services.Role{
|
|
ID: roleID,
|
|
Name: "test-role",
|
|
Description: "Test role description",
|
|
Permissions: []services.Permission{},
|
|
IsSystem: false,
|
|
}
|
|
|
|
mockService.On("CreateRole", mock.Anything, "test-role", "Test role description", []uuid.UUID{}).Return(expectedRole, nil)
|
|
|
|
reqBody := map[string]interface{}{
|
|
"name": "test-role",
|
|
"description": "Test role description",
|
|
"permissions": []uuid.UUID{},
|
|
}
|
|
body, _ := json.Marshal(reqBody)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/api/v1/roles", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusCreated, w.Code)
|
|
mockService.AssertExpectations(t)
|
|
|
|
var response map[string]interface{}
|
|
err := json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
assert.True(t, response["success"].(bool))
|
|
}
|
|
|
|
func TestRBACHandlers_CreateRole_InvalidJSON(t *testing.T) {
|
|
mockService := new(MockRBACService)
|
|
router := setupTestRBACRouter(mockService)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/api/v1/roles", bytes.NewBuffer([]byte("invalid json")))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
func TestRBACHandlers_CreateRole_MissingName(t *testing.T) {
|
|
mockService := new(MockRBACService)
|
|
router := setupTestRBACRouter(mockService)
|
|
|
|
reqBody := map[string]interface{}{
|
|
"description": "Test role description",
|
|
}
|
|
body, _ := json.Marshal(reqBody)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/api/v1/roles", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
func TestRBACHandlers_GetRole_Success(t *testing.T) {
|
|
mockService := new(MockRBACService)
|
|
router := setupTestRBACRouter(mockService)
|
|
|
|
roleID := uuid.New()
|
|
expectedRole := &services.Role{
|
|
ID: roleID,
|
|
Name: "test-role",
|
|
Description: "Test role description",
|
|
Permissions: []services.Permission{},
|
|
IsSystem: false,
|
|
}
|
|
|
|
mockService.On("GetRoleByID", mock.Anything, roleID).Return(expectedRole, nil)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("GET", "/api/v1/roles/"+roleID.String(), nil)
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
mockService.AssertExpectations(t)
|
|
|
|
var response map[string]interface{}
|
|
err := json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
assert.True(t, response["success"].(bool))
|
|
}
|
|
|
|
func TestRBACHandlers_GetRole_InvalidID(t *testing.T) {
|
|
mockService := new(MockRBACService)
|
|
router := setupTestRBACRouter(mockService)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("GET", "/api/v1/roles/invalid-id", nil)
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
func TestRBACHandlers_GetRole_NotFound(t *testing.T) {
|
|
mockService := new(MockRBACService)
|
|
router := setupTestRBACRouter(mockService)
|
|
|
|
roleID := uuid.New()
|
|
mockService.On("GetRoleByID", mock.Anything, roleID).Return(nil, assert.AnError)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("GET", "/api/v1/roles/"+roleID.String(), nil)
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusNotFound, w.Code)
|
|
mockService.AssertExpectations(t)
|
|
}
|
|
|
|
func TestRBACHandlers_GetAllRoles_Success(t *testing.T) {
|
|
mockService := new(MockRBACService)
|
|
router := setupTestRBACRouter(mockService)
|
|
|
|
expectedRoles := []*services.Role{
|
|
{
|
|
ID: uuid.New(),
|
|
Name: "role1",
|
|
Description: "Role 1",
|
|
Permissions: []services.Permission{},
|
|
IsSystem: false,
|
|
},
|
|
{
|
|
ID: uuid.New(),
|
|
Name: "role2",
|
|
Description: "Role 2",
|
|
Permissions: []services.Permission{},
|
|
IsSystem: false,
|
|
},
|
|
}
|
|
|
|
mockService.On("GetAllRoles", mock.Anything).Return(expectedRoles, nil)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("GET", "/api/v1/roles", nil)
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
mockService.AssertExpectations(t)
|
|
|
|
var response map[string]interface{}
|
|
err := json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
assert.True(t, response["success"].(bool))
|
|
}
|
|
|
|
func TestRBACHandlers_AssignRoleToUser_Success(t *testing.T) {
|
|
mockService := new(MockRBACService)
|
|
router := setupTestRBACRouter(mockService)
|
|
|
|
userID := uuid.New()
|
|
roleID := uuid.New()
|
|
|
|
mockService.On("AssignRoleToUser", mock.Anything, userID, roleID).Return(nil)
|
|
|
|
reqBody := map[string]interface{}{
|
|
"role_id": roleID.String(),
|
|
}
|
|
body, _ := json.Marshal(reqBody)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/api/v1/users/"+userID.String()+"/roles", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
mockService.AssertExpectations(t)
|
|
|
|
var response map[string]interface{}
|
|
err := json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
assert.True(t, response["success"].(bool))
|
|
}
|
|
|
|
func TestRBACHandlers_AssignRoleToUser_InvalidUserID(t *testing.T) {
|
|
mockService := new(MockRBACService)
|
|
router := setupTestRBACRouter(mockService)
|
|
|
|
roleID := uuid.New()
|
|
reqBody := map[string]interface{}{
|
|
"role_id": roleID.String(),
|
|
}
|
|
body, _ := json.Marshal(reqBody)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/api/v1/users/invalid-id/roles", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
func TestRBACHandlers_RemoveRoleFromUser_Success(t *testing.T) {
|
|
mockService := new(MockRBACService)
|
|
router := setupTestRBACRouter(mockService)
|
|
|
|
userID := uuid.New()
|
|
roleID := uuid.New()
|
|
|
|
mockService.On("RemoveRoleFromUser", mock.Anything, userID, roleID).Return(nil)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("DELETE", "/api/v1/users/"+userID.String()+"/roles/"+roleID.String(), nil)
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
mockService.AssertExpectations(t)
|
|
}
|
|
|
|
func TestRBACHandlers_GetUserRoles_Success(t *testing.T) {
|
|
mockService := new(MockRBACService)
|
|
router := setupTestRBACRouter(mockService)
|
|
|
|
userID := uuid.New()
|
|
expectedRoles := []*services.Role{
|
|
{
|
|
ID: uuid.New(),
|
|
Name: "admin",
|
|
Description: "Admin role",
|
|
Permissions: []services.Permission{},
|
|
IsSystem: true,
|
|
},
|
|
}
|
|
|
|
mockService.On("GetUserRoles", mock.Anything, userID).Return(expectedRoles, nil)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("GET", "/api/v1/users/"+userID.String()+"/roles", nil)
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
mockService.AssertExpectations(t)
|
|
|
|
var response map[string]interface{}
|
|
err := json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
assert.True(t, response["success"].(bool))
|
|
}
|
|
|
|
func TestRBACHandlers_GetUserPermissions_Success(t *testing.T) {
|
|
mockService := new(MockRBACService)
|
|
router := setupTestRBACRouter(mockService)
|
|
|
|
userID := uuid.New()
|
|
expectedPermissions := []services.Permission{
|
|
{
|
|
ID: uuid.New(),
|
|
Name: "read:tracks",
|
|
Description: "Read tracks",
|
|
Resource: "tracks",
|
|
Action: "read",
|
|
},
|
|
}
|
|
|
|
mockService.On("GetUserPermissions", mock.Anything, userID).Return(expectedPermissions, nil)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("GET", "/api/v1/users/"+userID.String()+"/permissions", nil)
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
mockService.AssertExpectations(t)
|
|
|
|
var response map[string]interface{}
|
|
err := json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
assert.True(t, response["success"].(bool))
|
|
}
|
|
|
|
func TestRBACHandlers_CheckPermission_Success(t *testing.T) {
|
|
mockService := new(MockRBACService)
|
|
router := setupTestRBACRouter(mockService)
|
|
|
|
userID := uuid.New()
|
|
mockService.On("CheckPermission", mock.Anything, userID, "tracks", "read").Return(true, nil)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("GET", "/api/v1/users/"+userID.String()+"/permissions/check?resource=tracks&action=read", nil)
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
mockService.AssertExpectations(t)
|
|
|
|
var response map[string]interface{}
|
|
err := json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
assert.True(t, response["success"].(bool))
|
|
assert.True(t, response["has_permission"].(bool))
|
|
}
|
|
|
|
func TestRBACHandlers_CheckPermission_MissingParams(t *testing.T) {
|
|
mockService := new(MockRBACService)
|
|
router := setupTestRBACRouter(mockService)
|
|
|
|
userID := uuid.New()
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("GET", "/api/v1/users/"+userID.String()+"/permissions/check?resource=tracks", nil)
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
func TestRBACHandlers_CreatePermission_Success(t *testing.T) {
|
|
mockService := new(MockRBACService)
|
|
router := setupTestRBACRouter(mockService)
|
|
|
|
permissionID := uuid.New()
|
|
expectedPermission := &services.Permission{
|
|
ID: permissionID,
|
|
Name: "read:tracks",
|
|
Description: "Read tracks",
|
|
Resource: "tracks",
|
|
Action: "read",
|
|
}
|
|
|
|
mockService.On("CreatePermission", mock.Anything, "read:tracks", "Read tracks", "tracks", "read").Return(expectedPermission, nil)
|
|
|
|
reqBody := map[string]interface{}{
|
|
"name": "read:tracks",
|
|
"description": "Read tracks",
|
|
"resource": "tracks",
|
|
"action": "read",
|
|
}
|
|
body, _ := json.Marshal(reqBody)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/api/v1/permissions", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusCreated, w.Code)
|
|
mockService.AssertExpectations(t)
|
|
|
|
var response map[string]interface{}
|
|
err := json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
assert.True(t, response["success"].(bool))
|
|
}
|
|
|
|
func TestRBACHandlers_CreatePermission_MissingFields(t *testing.T) {
|
|
mockService := new(MockRBACService)
|
|
router := setupTestRBACRouter(mockService)
|
|
|
|
reqBody := map[string]interface{}{
|
|
"name": "read:tracks",
|
|
// Missing required fields: resource, action
|
|
}
|
|
body, _ := json.Marshal(reqBody)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/api/v1/permissions", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|