From 1aeb7afb13696663408f48b615dd278e992b1ff6 Mon Sep 17 00:00:00 2001 From: senke Date: Tue, 21 Apr 2026 09:50:43 +0200 Subject: [PATCH] chore(cleanup): remove 3 deprecated handlers from internal/api/handlers/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `internal/api/handlers/` package held only 3 files, all flagged DEPRECATED in the audit and never imported anywhere: - chat_handlers.go (376 LOC, replaced by internal/handlers/ + internal/websocket/chat/ when Rust chat server was removed 2026-02-22) - rbac_handlers.go (278 LOC, replaced by internal/core/admin/ role management) - rbac_handlers_test.go (488 LOC) Verified via grep: `internal/api/handlers` has zero imports across the backend. `go build ./...` and `go vet` clean after removal. Directory is now empty and automatically pruned by git. -1142 LOC of dead code gone. Refs: AUDIT_REPORT.md ยง8.2 "Code mort / orphelin". Co-Authored-By: Claude Opus 4.7 (1M context) --- .../internal/api/handlers/chat_handlers.go | 376 -------------- .../internal/api/handlers/rbac_handlers.go | 278 ---------- .../api/handlers/rbac_handlers_test.go | 488 ------------------ 3 files changed, 1142 deletions(-) delete mode 100644 veza-backend-api/internal/api/handlers/chat_handlers.go delete mode 100644 veza-backend-api/internal/api/handlers/rbac_handlers.go delete mode 100644 veza-backend-api/internal/api/handlers/rbac_handlers_test.go diff --git a/veza-backend-api/internal/api/handlers/chat_handlers.go b/veza-backend-api/internal/api/handlers/chat_handlers.go deleted file mode 100644 index bd61c0eae..000000000 --- a/veza-backend-api/internal/api/handlers/chat_handlers.go +++ /dev/null @@ -1,376 +0,0 @@ -//go:build ignore -// +build ignore - -// NOTE: Disabled (build ignore). Chat server removed; re-enable when chat service is reimplemented. - -package handlers - -import ( - "net/http" - "strconv" - - "github.com/gin-gonic/gin" - "go.uber.org/zap" - - "veza-backend-api/internal/services" -) - -// ChatHandlers handles chat-related API endpoints -type ChatHandlers struct { - chatService *services.ChatService - logger *zap.Logger -} - -// NewChatHandlers creates new chat handlers -func NewChatHandlers(chatService *services.ChatService, logger *zap.Logger) *ChatHandlers { - return &ChatHandlers{ - chatService: chatService, - logger: logger, - } -} - -// InitChatHandlers initializes chat handlers -func InitChatHandlers(chatService *services.ChatService, logger *zap.Logger) { - handlers := NewChatHandlers(chatService, logger) - - // Store handlers globally for route registration - ChatHandlersInstance = handlers -} - -// ChatHandlersInstance holds the global chat handlers instance -var ChatHandlersInstance *ChatHandlers - -// CreateMessage creates a new message in a room -func (h *ChatHandlers) CreateMessage(c *gin.Context) { - userID := c.GetInt64("user_id") - roomID, err := strconv.ParseInt(c.Param("room_id"), 10, 64) - if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid room ID"}) - return - } - - var req struct { - Content string `json:"content" binding:"required"` - Type services.MessageType `json:"type"` - ParentID *int64 `json:"parent_id,omitempty"` - } - - if err := c.ShouldBindJSON(&req); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - return - } - - if req.Type == "" { - req.Type = services.MessageTypeText - } - - message, err := h.chatService.CreateMessage(c.Request.Context(), roomID, userID, req.Content, req.Type, req.ParentID) - if err != nil { - h.logger.Error("Failed to create message", zap.Error(err)) - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create message"}) - return - } - - c.JSON(http.StatusCreated, gin.H{ - "success": true, - "message": message, - }) -} - -// GetMessages retrieves messages for a room -func (h *ChatHandlers) GetMessages(c *gin.Context) { - roomID, err := strconv.ParseInt(c.Param("room_id"), 10, 64) - if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid room ID"}) - return - } - - page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) - limit, _ := strconv.Atoi(c.DefaultQuery("limit", "50")) - beforeIDStr := c.Query("before_id") - - var beforeID *int64 - if beforeIDStr != "" { - if id, err := strconv.ParseInt(beforeIDStr, 10, 64); err == nil { - beforeID = &id - } - } - - messages, err := h.chatService.GetMessages(c.Request.Context(), roomID, page, limit, beforeID) - if err != nil { - h.logger.Error("Failed to get messages", zap.Error(err)) - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get messages"}) - return - } - - c.JSON(http.StatusOK, gin.H{ - "success": true, - "messages": messages, - "page": page, - "limit": limit, - }) -} - -// AddReaction adds a reaction to a message -func (h *ChatHandlers) AddReaction(c *gin.Context) { - userID := c.GetInt64("user_id") - messageID, err := strconv.ParseInt(c.Param("message_id"), 10, 64) - if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid message ID"}) - return - } - - var req struct { - Emoji string `json:"emoji" binding:"required"` - } - - if err := c.ShouldBindJSON(&req); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - return - } - - reaction, err := h.chatService.AddReaction(c.Request.Context(), messageID, userID, req.Emoji) - if err != nil { - h.logger.Error("Failed to add reaction", zap.Error(err)) - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to add reaction"}) - return - } - - c.JSON(http.StatusCreated, gin.H{ - "success": true, - "reaction": reaction, - }) -} - -// RemoveReaction removes a reaction from a message -func (h *ChatHandlers) RemoveReaction(c *gin.Context) { - userID := c.GetInt64("user_id") - messageID, err := strconv.ParseInt(c.Param("message_id"), 10, 64) - if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid message ID"}) - return - } - - emoji := c.Param("emoji") - if emoji == "" { - c.JSON(http.StatusBadRequest, gin.H{"error": "Emoji is required"}) - return - } - - err = h.chatService.RemoveReaction(c.Request.Context(), messageID, userID, emoji) - if err != nil { - h.logger.Error("Failed to remove reaction", zap.Error(err)) - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to remove reaction"}) - return - } - - c.JSON(http.StatusOK, gin.H{ - "success": true, - "message": "Reaction removed", - }) -} - -// CreateRoom creates a new chat room -func (h *ChatHandlers) CreateRoom(c *gin.Context) { - userID := c.GetInt64("user_id") - - var req struct { - Name string `json:"name" binding:"required"` - Description string `json:"description"` - Type services.RoomType `json:"type"` - IsPrivate bool `json:"is_private"` - } - - if err := c.ShouldBindJSON(&req); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - return - } - - if req.Type == "" { - req.Type = services.RoomTypePublic - } - - room, err := h.chatService.CreateRoom(c.Request.Context(), req.Name, req.Description, req.Type, req.IsPrivate, userID) - if err != nil { - h.logger.Error("Failed to create room", zap.Error(err)) - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create room"}) - return - } - - c.JSON(http.StatusCreated, gin.H{ - "success": true, - "room": room, - }) -} - -// GetRooms retrieves available rooms -func (h *ChatHandlers) GetRooms(c *gin.Context) { - userID := c.GetInt64("user_id") - includePrivate := c.DefaultQuery("include_private", "false") == "true" - - rooms, err := h.chatService.GetRooms(c.Request.Context(), userID, includePrivate) - if err != nil { - h.logger.Error("Failed to get rooms", zap.Error(err)) - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get rooms"}) - return - } - - c.JSON(http.StatusOK, gin.H{ - "success": true, - "rooms": rooms, - }) -} - -// JoinRoom adds a user to a room -func (h *ChatHandlers) JoinRoom(c *gin.Context) { - userID := c.GetInt64("user_id") - roomID, err := strconv.ParseInt(c.Param("room_id"), 10, 64) - if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid room ID"}) - return - } - - err = h.chatService.JoinRoom(c.Request.Context(), roomID, userID) - if err != nil { - h.logger.Error("Failed to join room", zap.Error(err)) - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to join room"}) - return - } - - c.JSON(http.StatusOK, gin.H{ - "success": true, - "message": "Successfully joined room", - }) -} - -// LeaveRoom removes a user from a room -func (h *ChatHandlers) LeaveRoom(c *gin.Context) { - userID := c.GetInt64("user_id") - roomID, err := strconv.ParseInt(c.Param("room_id"), 10, 64) - if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid room ID"}) - return - } - - err = h.chatService.LeaveRoom(c.Request.Context(), roomID, userID) - if err != nil { - h.logger.Error("Failed to leave room", zap.Error(err)) - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to leave room"}) - return - } - - c.JSON(http.StatusOK, gin.H{ - "success": true, - "message": "Successfully left room", - }) -} - -// CreateDirectMessage creates a DM room between two users -func (h *ChatHandlers) CreateDirectMessage(c *gin.Context) { - userID := c.GetInt64("user_id") - - var req struct { - UserID int64 `json:"user_id" binding:"required"` - } - - if err := c.ShouldBindJSON(&req); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - return - } - - room, err := h.chatService.CreateDirectMessage(c.Request.Context(), userID, req.UserID) - if err != nil { - h.logger.Error("Failed to create DM", zap.Error(err)) - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create direct message"}) - return - } - - c.JSON(http.StatusCreated, gin.H{ - "success": true, - "room": room, - }) -} - -// SearchMessages searches for messages in a room -func (h *ChatHandlers) SearchMessages(c *gin.Context) { - roomID, err := strconv.ParseInt(c.Param("room_id"), 10, 64) - if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid room ID"}) - return - } - - query := c.Query("q") - if query == "" { - c.JSON(http.StatusBadRequest, gin.H{"error": "Search query is required"}) - return - } - - limit, _ := strconv.Atoi(c.DefaultQuery("limit", "20")) - - messages, err := h.chatService.SearchMessages(c.Request.Context(), roomID, query, limit) - if err != nil { - h.logger.Error("Failed to search messages", zap.Error(err)) - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to search messages"}) - return - } - - c.JSON(http.StatusOK, gin.H{ - "success": true, - "messages": messages, - "query": query, - "limit": limit, - }) -} - -// EditMessage edits an existing message -func (h *ChatHandlers) EditMessage(c *gin.Context) { - userID := c.GetInt64("user_id") - messageID, err := strconv.ParseInt(c.Param("message_id"), 10, 64) - if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid message ID"}) - return - } - - var req struct { - Content string `json:"content" binding:"required"` - } - - if err := c.ShouldBindJSON(&req); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - return - } - - message, err := h.chatService.EditMessage(c.Request.Context(), messageID, userID, req.Content) - if err != nil { - h.logger.Error("Failed to edit message", zap.Error(err)) - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to edit message"}) - return - } - - c.JSON(http.StatusOK, gin.H{ - "success": true, - "message": message, - }) -} - -// DeleteMessage deletes a message -func (h *ChatHandlers) DeleteMessage(c *gin.Context) { - userID := c.GetInt64("user_id") - messageID, err := strconv.ParseInt(c.Param("message_id"), 10, 64) - if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid message ID"}) - return - } - - err = h.chatService.DeleteMessage(c.Request.Context(), messageID, userID) - if err != nil { - h.logger.Error("Failed to delete message", zap.Error(err)) - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete message"}) - return - } - - c.JSON(http.StatusOK, gin.H{ - "success": true, - "message": "Message deleted successfully", - }) -} diff --git a/veza-backend-api/internal/api/handlers/rbac_handlers.go b/veza-backend-api/internal/api/handlers/rbac_handlers.go deleted file mode 100644 index 3dc663423..000000000 --- a/veza-backend-api/internal/api/handlers/rbac_handlers.go +++ /dev/null @@ -1,278 +0,0 @@ -package handlers - -import ( - "context" - "net/http" - - "github.com/gin-gonic/gin" - "github.com/google/uuid" - "go.uber.org/zap" - - "veza-backend-api/internal/services" -) - -// RBACServiceInterface defines the interface for RBAC operations -// This allows for easier testing with mocks -type RBACServiceInterface interface { - CreateRole(ctx context.Context, name, description string, permissions []uuid.UUID) (*services.Role, error) - GetRoleByID(ctx context.Context, roleID uuid.UUID) (*services.Role, error) - GetAllRoles(ctx context.Context) ([]*services.Role, error) - AssignRoleToUser(ctx context.Context, userID, roleID uuid.UUID) error - RemoveRoleFromUser(ctx context.Context, userID, roleID uuid.UUID) error - GetUserRoles(ctx context.Context, userID uuid.UUID) ([]*services.Role, error) - GetUserPermissions(ctx context.Context, userID uuid.UUID) ([]services.Permission, error) - CheckPermission(ctx context.Context, userID uuid.UUID, resource, action string) (bool, error) - CreatePermission(ctx context.Context, name, description, resource, action string) (*services.Permission, error) -} - -// RBACHandlers handles RBAC-related API endpoints -type RBACHandlers struct { - rbacService RBACServiceInterface - logger *zap.Logger -} - -// NewRBACHandlers creates new RBAC handlers -func NewRBACHandlers(rbacService *services.RBACService, logger *zap.Logger) *RBACHandlers { - return &RBACHandlers{ - rbacService: rbacService, - logger: logger, - } -} - -// NewRBACHandlersWithInterface creates new RBAC handlers with an interface (for testing) -func NewRBACHandlersWithInterface(rbacService RBACServiceInterface, logger *zap.Logger) *RBACHandlers { - return &RBACHandlers{ - rbacService: rbacService, - logger: logger, - } -} - -// InitRBACHandlers initializes RBAC handlers -func InitRBACHandlers(rbacService *services.RBACService, logger *zap.Logger) { - handlers := NewRBACHandlers(rbacService, logger) - - // Store handlers globally for route registration - RBACHandlersInstance = handlers -} - -// RBACHandlersInstance holds the global RBAC handlers instance -var RBACHandlersInstance *RBACHandlers - -// CreateRole creates a new role -func (h *RBACHandlers) CreateRole(c *gin.Context) { - var req struct { - Name string `json:"name" binding:"required"` - Description string `json:"description"` - Permissions []uuid.UUID `json:"permissions"` - } - - if err := c.ShouldBindJSON(&req); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - return - } - - role, err := h.rbacService.CreateRole(c.Request.Context(), req.Name, req.Description, req.Permissions) - if err != nil { - h.logger.Error("Failed to create role", zap.Error(err)) - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create role"}) - return - } - - c.JSON(http.StatusCreated, gin.H{ - "success": true, - "role": role, - }) -} - -// GetRole gets a role by ID -func (h *RBACHandlers) GetRole(c *gin.Context) { - roleID, err := uuid.Parse(c.Param("id")) - if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid role ID"}) - return - } - - role, err := h.rbacService.GetRoleByID(c.Request.Context(), roleID) - if err != nil { - h.logger.Error("Failed to get role", zap.Error(err)) - c.JSON(http.StatusNotFound, gin.H{"error": "Role not found"}) - return - } - - c.JSON(http.StatusOK, gin.H{ - "success": true, - "role": role, - }) -} - -// GetAllRoles gets all roles -func (h *RBACHandlers) GetAllRoles(c *gin.Context) { - roles, err := h.rbacService.GetAllRoles(c.Request.Context()) - if err != nil { - h.logger.Error("Failed to get roles", zap.Error(err)) - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get roles"}) - return - } - - c.JSON(http.StatusOK, gin.H{ - "success": true, - "roles": roles, - }) -} - -// AssignRoleToUser assigns a role to a user -func (h *RBACHandlers) AssignRoleToUser(c *gin.Context) { - userID, err := uuid.Parse(c.Param("user_id")) - if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID"}) - return - } - - var req struct { - RoleID uuid.UUID `json:"role_id" binding:"required"` - } - - if err := c.ShouldBindJSON(&req); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - return - } - - err = h.rbacService.AssignRoleToUser(c.Request.Context(), userID, req.RoleID) - if err != nil { - h.logger.Error("Failed to assign role to user", zap.Error(err)) - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to assign role to user"}) - return - } - - c.JSON(http.StatusOK, gin.H{ - "success": true, - "message": "Role assigned to user successfully", - }) -} - -// RemoveRoleFromUser removes a role from a user -func (h *RBACHandlers) RemoveRoleFromUser(c *gin.Context) { - userID, err := uuid.Parse(c.Param("user_id")) - if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID"}) - return - } - - roleID, err := uuid.Parse(c.Param("role_id")) - if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid role ID"}) - return - } - - err = h.rbacService.RemoveRoleFromUser(c.Request.Context(), userID, roleID) - if err != nil { - h.logger.Error("Failed to remove role from user", zap.Error(err)) - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to remove role from user"}) - return - } - - c.JSON(http.StatusOK, gin.H{ - "success": true, - "message": "Role removed from user successfully", - }) -} - -// GetUserRoles gets all roles for a user -func (h *RBACHandlers) GetUserRoles(c *gin.Context) { - userID, err := uuid.Parse(c.Param("user_id")) - if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID"}) - return - } - - roles, err := h.rbacService.GetUserRoles(c.Request.Context(), userID) - if err != nil { - h.logger.Error("Failed to get user roles", zap.Error(err)) - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get user roles"}) - return - } - - c.JSON(http.StatusOK, gin.H{ - "success": true, - "roles": roles, - }) -} - -// GetUserPermissions gets all permissions for a user -func (h *RBACHandlers) GetUserPermissions(c *gin.Context) { - userID, err := uuid.Parse(c.Param("user_id")) - if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID"}) - return - } - - permissions, err := h.rbacService.GetUserPermissions(c.Request.Context(), userID) - if err != nil { - h.logger.Error("Failed to get user permissions", zap.Error(err)) - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get user permissions"}) - return - } - - c.JSON(http.StatusOK, gin.H{ - "success": true, - "permissions": permissions, - }) -} - -// CheckPermission checks if a user has a specific permission -func (h *RBACHandlers) CheckPermission(c *gin.Context) { - userID, err := uuid.Parse(c.Param("user_id")) - if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID"}) - return - } - - resource := c.Query("resource") - action := c.Query("action") - - if resource == "" || action == "" { - c.JSON(http.StatusBadRequest, gin.H{"error": "Resource and action are required"}) - return - } - - hasPermission, err := h.rbacService.CheckPermission(c.Request.Context(), userID, resource, action) - if err != nil { - h.logger.Error("Failed to check permission", zap.Error(err)) - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to check permission"}) - return - } - - c.JSON(http.StatusOK, gin.H{ - "success": true, - "has_permission": hasPermission, - "resource": resource, - "action": action, - }) -} - -// CreatePermission creates a new permission -func (h *RBACHandlers) CreatePermission(c *gin.Context) { - var req struct { - Name string `json:"name" binding:"required"` - Description string `json:"description"` - Resource string `json:"resource" binding:"required"` - Action string `json:"action" binding:"required"` - } - - if err := c.ShouldBindJSON(&req); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - return - } - - permission, err := h.rbacService.CreatePermission(c.Request.Context(), req.Name, req.Description, req.Resource, req.Action) - if err != nil { - h.logger.Error("Failed to create permission", zap.Error(err)) - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create permission"}) - return - } - - c.JSON(http.StatusCreated, gin.H{ - "success": true, - "permission": permission, - }) -} diff --git a/veza-backend-api/internal/api/handlers/rbac_handlers_test.go b/veza-backend-api/internal/api/handlers/rbac_handlers_test.go deleted file mode 100644 index 4f1fc6063..000000000 --- a/veza-backend-api/internal/api/handlers/rbac_handlers_test.go +++ /dev/null @@ -1,488 +0,0 @@ -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) -}