package services import ( "context" "testing" "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "gorm.io/driver/sqlite" "gorm.io/gorm" "veza-backend-api/internal/models" ) func setupTestRoleService(t *testing.T) (*RoleService, *gorm.DB, func()) { db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{}) require.NoError(t, err) db.Exec("PRAGMA foreign_keys = ON") err = db.AutoMigrate( &models.User{}, &models.Role{}, &models.Permission{}, &models.UserRole{}, &models.RolePermission{}, ) require.NoError(t, err) service := NewRoleService(db) cleanup := func() { sqlDB, _ := db.DB() if sqlDB != nil { sqlDB.Close() } } return service, db, cleanup } func TestRoleService_NewRoleService(t *testing.T) { _, db, cleanup := setupTestRoleService(t) defer cleanup() service := NewRoleService(db) assert.NotNil(t, service) assert.NotNil(t, service.db) } func TestRoleService_GetRoles_Empty(t *testing.T) { service, _, cleanup := setupTestRoleService(t) defer cleanup() ctx := context.Background() roles, err := service.GetRoles(ctx) assert.NoError(t, err) assert.NotNil(t, roles) assert.Len(t, roles, 0) } func TestRoleService_CreateRole_Success(t *testing.T) { service, db, cleanup := setupTestRoleService(t) defer cleanup() ctx := context.Background() role := &models.Role{ Name: "test_role", Description: "Test role description", IsSystem: false, } err := service.CreateRole(ctx, role) assert.NoError(t, err) assert.NotEqual(t, uuid.Nil, role.ID) // Verify role was created var createdRole models.Role err = db.First(&createdRole, role.ID).Error assert.NoError(t, err) assert.Equal(t, "test_role", createdRole.Name) assert.Equal(t, "Test role description", createdRole.Description) assert.False(t, createdRole.IsSystem) } func TestRoleService_GetRole_Success(t *testing.T) { service, db, cleanup := setupTestRoleService(t) defer cleanup() ctx := context.Background() role := &models.Role{ Name: "test_role", Description: "Test role description", IsSystem: false, } err := db.Create(role).Error require.NoError(t, err) retrievedRole, err := service.GetRole(ctx, role.ID) assert.NoError(t, err) assert.NotNil(t, retrievedRole) assert.Equal(t, role.ID, retrievedRole.ID) assert.Equal(t, "test_role", retrievedRole.Name) } func TestRoleService_GetRole_NotFound(t *testing.T) { service, _, cleanup := setupTestRoleService(t) defer cleanup() ctx := context.Background() nonExistentID := uuid.New() role, err := service.GetRole(ctx, nonExistentID) assert.Error(t, err) assert.Nil(t, role) assert.Contains(t, err.Error(), "role not found") } func TestRoleService_UpdateRole_Success(t *testing.T) { service, db, cleanup := setupTestRoleService(t) defer cleanup() ctx := context.Background() role := &models.Role{ Name: "test_role", Description: "Original description", IsSystem: false, } err := db.Create(role).Error require.NoError(t, err) updates := &models.Role{ Name: "updated_role", Description: "Updated description", } err = service.UpdateRole(ctx, role.ID, updates) assert.NoError(t, err) // Verify role was updated var updatedRole models.Role err = db.First(&updatedRole, role.ID).Error assert.NoError(t, err) assert.Equal(t, "updated_role", updatedRole.Name) assert.Equal(t, "Updated description", updatedRole.Description) } func TestRoleService_UpdateRole_SystemRole(t *testing.T) { service, db, cleanup := setupTestRoleService(t) defer cleanup() ctx := context.Background() role := &models.Role{ Name: "system_role", Description: "System role", IsSystem: true, } err := db.Create(role).Error require.NoError(t, err) updates := &models.Role{ Name: "updated_system_role", } err = service.UpdateRole(ctx, role.ID, updates) assert.Error(t, err) assert.Contains(t, err.Error(), "role not found or is system role") } func TestRoleService_UpdateRole_NotFound(t *testing.T) { service, _, cleanup := setupTestRoleService(t) defer cleanup() ctx := context.Background() nonExistentID := uuid.New() updates := &models.Role{ Name: "updated_role", } err := service.UpdateRole(ctx, nonExistentID, updates) assert.Error(t, err) assert.Contains(t, err.Error(), "role not found or is system role") } func TestRoleService_DeleteRole_Success(t *testing.T) { service, db, cleanup := setupTestRoleService(t) defer cleanup() ctx := context.Background() role := &models.Role{ Name: "test_role", Description: "Test role", IsSystem: false, } err := db.Create(role).Error require.NoError(t, err) err = service.DeleteRole(ctx, role.ID) assert.NoError(t, err) // Verify role was deleted var deletedRole models.Role err = db.First(&deletedRole, role.ID).Error assert.Error(t, err) assert.Equal(t, gorm.ErrRecordNotFound, err) } func TestRoleService_DeleteRole_SystemRole(t *testing.T) { service, db, cleanup := setupTestRoleService(t) defer cleanup() ctx := context.Background() role := &models.Role{ Name: "system_role", Description: "System role", IsSystem: true, } err := db.Create(role).Error require.NoError(t, err) err = service.DeleteRole(ctx, role.ID) assert.Error(t, err) assert.Contains(t, err.Error(), "cannot delete system role") } func TestRoleService_DeleteRole_NotFound(t *testing.T) { service, _, cleanup := setupTestRoleService(t) defer cleanup() ctx := context.Background() nonExistentID := uuid.New() err := service.DeleteRole(ctx, nonExistentID) assert.Error(t, err) assert.Contains(t, err.Error(), "role not found") } func TestRoleService_AssignRoleToUser_Success(t *testing.T) { service, db, cleanup := setupTestRoleService(t) defer cleanup() ctx := context.Background() user := &models.User{ Username: "testuser", Email: "test@example.com", PasswordHash: "hash", IsActive: true, } err := db.Create(user).Error require.NoError(t, err) role := &models.Role{ Name: "test_role", Description: "Test role", IsSystem: false, } err = db.Create(role).Error require.NoError(t, err) assignedBy := uuid.New() err = service.AssignRoleToUser(ctx, user.ID, role.ID, assignedBy, nil) assert.NoError(t, err) // Verify role was assigned var userRole models.UserRole err = db.Where("user_id = ? AND role_id = ?", user.ID, role.ID).First(&userRole).Error assert.NoError(t, err) assert.True(t, userRole.IsActive) } func TestRoleService_RevokeRoleFromUser_Success(t *testing.T) { service, db, cleanup := setupTestRoleService(t) defer cleanup() ctx := context.Background() user := &models.User{ Username: "testuser", Email: "test@example.com", PasswordHash: "hash", IsActive: true, } err := db.Create(user).Error require.NoError(t, err) role := &models.Role{ Name: "test_role", Description: "Test role", IsSystem: false, } err = db.Create(role).Error require.NoError(t, err) assignedBy := uuid.New() err = service.AssignRoleToUser(ctx, user.ID, role.ID, assignedBy, nil) require.NoError(t, err) err = service.RevokeRoleFromUser(ctx, user.ID, role.ID) assert.NoError(t, err) // Verify role was revoked var userRole models.UserRole err = db.Where("user_id = ? AND role_id = ?", user.ID, role.ID).First(&userRole).Error assert.NoError(t, err) assert.False(t, userRole.IsActive) } func TestRoleService_RevokeRoleFromUser_NotFound(t *testing.T) { service, db, cleanup := setupTestRoleService(t) defer cleanup() ctx := context.Background() user := &models.User{ Username: "testuser", Email: "test@example.com", PasswordHash: "hash", IsActive: true, } err := db.Create(user).Error require.NoError(t, err) role := &models.Role{ Name: "test_role", Description: "Test role", IsSystem: false, } err = db.Create(role).Error require.NoError(t, err) err = service.RevokeRoleFromUser(ctx, user.ID, role.ID) assert.Error(t, err) assert.Contains(t, err.Error(), "role assignment not found") } func TestRoleService_GetUserRoles_Success(t *testing.T) { service, db, cleanup := setupTestRoleService(t) defer cleanup() ctx := context.Background() user := &models.User{ Username: "testuser", Email: "test@example.com", PasswordHash: "hash", IsActive: true, } err := db.Create(user).Error require.NoError(t, err) role1 := &models.Role{ Name: "role1", Description: "Role 1", IsSystem: false, } err = db.Create(role1).Error require.NoError(t, err) role2 := &models.Role{ Name: "role2", Description: "Role 2", IsSystem: false, } err = db.Create(role2).Error require.NoError(t, err) assignedBy := uuid.New() err = service.AssignRoleToUser(ctx, user.ID, role1.ID, assignedBy, nil) require.NoError(t, err) err = service.AssignRoleToUser(ctx, user.ID, role2.ID, assignedBy, nil) require.NoError(t, err) roles, err := service.GetUserRoles(ctx, user.ID) assert.NoError(t, err) assert.Len(t, roles, 2) // Both roles should be active } func TestRoleService_GetUserRoles_Empty(t *testing.T) { service, db, cleanup := setupTestRoleService(t) defer cleanup() ctx := context.Background() user := &models.User{ Username: "testuser", Email: "test@example.com", PasswordHash: "hash", IsActive: true, } err := db.Create(user).Error require.NoError(t, err) roles, err := service.GetUserRoles(ctx, user.ID) assert.NoError(t, err) assert.Len(t, roles, 0) } func TestRoleService_HasRole_True(t *testing.T) { service, db, cleanup := setupTestRoleService(t) defer cleanup() ctx := context.Background() user := &models.User{ Username: "testuser", Email: "test@example.com", PasswordHash: "hash", IsActive: true, } err := db.Create(user).Error require.NoError(t, err) role := &models.Role{ Name: "test_role", Description: "Test role", IsSystem: false, } err = db.Create(role).Error require.NoError(t, err) assignedBy := uuid.New() err = service.AssignRoleToUser(ctx, user.ID, role.ID, assignedBy, nil) require.NoError(t, err) hasRole, err := service.HasRole(ctx, user.ID, "test_role") assert.NoError(t, err) assert.True(t, hasRole) } func TestRoleService_HasRole_False(t *testing.T) { service, db, cleanup := setupTestRoleService(t) defer cleanup() ctx := context.Background() user := &models.User{ Username: "testuser", Email: "test@example.com", PasswordHash: "hash", IsActive: true, } err := db.Create(user).Error require.NoError(t, err) hasRole, err := service.HasRole(ctx, user.ID, "non_existent_role") assert.NoError(t, err) assert.False(t, hasRole) } func TestRoleService_HasPermission_True(t *testing.T) { service, db, cleanup := setupTestRoleService(t) defer cleanup() ctx := context.Background() user := &models.User{ Username: "testuser", Email: "test@example.com", PasswordHash: "hash", IsActive: true, } err := db.Create(user).Error require.NoError(t, err) role := &models.Role{ Name: "test_role", Description: "Test role", IsSystem: false, } err = db.Create(role).Error require.NoError(t, err) permission := &models.Permission{ Name: "test_permission", Description: "Test permission", Resource: "tracks", Action: "read", } err = db.Create(permission).Error require.NoError(t, err) rolePermission := &models.RolePermission{ RoleID: role.ID, PermissionID: permission.ID, } err = db.Create(rolePermission).Error require.NoError(t, err) assignedBy := uuid.New() err = service.AssignRoleToUser(ctx, user.ID, role.ID, assignedBy, nil) require.NoError(t, err) hasPermission, err := service.HasPermission(ctx, user.ID, "tracks", "read") assert.NoError(t, err) assert.True(t, hasPermission) } func TestRoleService_HasPermission_False(t *testing.T) { service, db, cleanup := setupTestRoleService(t) defer cleanup() ctx := context.Background() user := &models.User{ Username: "testuser", Email: "test@example.com", PasswordHash: "hash", IsActive: true, } err := db.Create(user).Error require.NoError(t, err) hasPermission, err := service.HasPermission(ctx, user.ID, "tracks", "write") assert.NoError(t, err) assert.False(t, hasPermission) } func TestRoleService_GetRoles_WithPermissions(t *testing.T) { service, db, cleanup := setupTestRoleService(t) defer cleanup() ctx := context.Background() role := &models.Role{ Name: "test_role", Description: "Test role", IsSystem: false, } err := db.Create(role).Error require.NoError(t, err) permission := &models.Permission{ Name: "test_permission", Description: "Test permission", Resource: "tracks", Action: "read", } err = db.Create(permission).Error require.NoError(t, err) rolePermission := &models.RolePermission{ RoleID: role.ID, PermissionID: permission.ID, } err = db.Create(rolePermission).Error require.NoError(t, err) roles, err := service.GetRoles(ctx) assert.NoError(t, err) assert.Len(t, roles, 1) assert.Len(t, roles[0].Permissions, 1) assert.Equal(t, "test_permission", roles[0].Permissions[0].Name) } func TestRoleService_GetRole_WithPermissions(t *testing.T) { service, db, cleanup := setupTestRoleService(t) defer cleanup() ctx := context.Background() role := &models.Role{ Name: "test_role", Description: "Test role", IsSystem: false, } err := db.Create(role).Error require.NoError(t, err) permission := &models.Permission{ Name: "test_permission", Description: "Test permission", Resource: "tracks", Action: "read", } err = db.Create(permission).Error require.NoError(t, err) rolePermission := &models.RolePermission{ RoleID: role.ID, PermissionID: permission.ID, } err = db.Create(rolePermission).Error require.NoError(t, err) retrievedRole, err := service.GetRole(ctx, role.ID) assert.NoError(t, err) assert.NotNil(t, retrievedRole) assert.Len(t, retrievedRole.Permissions, 1) assert.Equal(t, "test_permission", retrievedRole.Permissions[0].Name) }