Files
backend/internal/service/role_service_test.go

222 lines
6.4 KiB
Go
Raw Normal View History

package service
import (
"context"
"errors"
"testing"
apperrors "with_you/internal/errors"
"with_you/internal/model"
"github.com/google/uuid"
"gorm.io/gorm"
)
// fakeRoleRepo 实现 repository.RoleRepository 接口,用于测试。
type fakeRoleRepo struct {
roles map[string]bool // userID -> role set不支持多角色测试足够
addErr error
getRoleErr error
rolesForUser []string
getRoleByName *model.Role
}
func (f *fakeRoleRepo) GetUserRoles(ctx context.Context, userID string) ([]string, error) {
return f.rolesForUser, nil
}
func (f *fakeRoleRepo) AddRoleForUser(ctx context.Context, userID, role string) error {
if f.addErr != nil {
return f.addErr
}
if f.roles == nil {
f.roles = make(map[string]bool)
}
f.roles[userID+":"+role] = true
return nil
}
func (f *fakeRoleRepo) DeleteRoleForUser(ctx context.Context, userID, role string) error {
if f.roles == nil {
return nil
}
delete(f.roles, userID+":"+role)
return nil
}
func (f *fakeRoleRepo) DeleteAllRolesForUser(ctx context.Context, userID string) error {
return nil
}
func (f *fakeRoleRepo) HasRole(ctx context.Context, userID, role string) (bool, error) {
if f.roles == nil {
return false, nil
}
return f.roles[userID+":"+role], nil
}
func (f *fakeRoleRepo) GetRole(ctx context.Context, name string) (*model.Role, error) {
if f.getRoleErr != nil {
return nil, f.getRoleErr
}
if f.getRoleByName != nil && f.getRoleByName.Name == name {
return f.getRoleByName, nil
}
return &model.Role{Name: name}, nil
}
func (f *fakeRoleRepo) GetAllRoles(ctx context.Context) ([]model.Role, error) {
return nil, nil
}
func (f *fakeRoleRepo) GetRoleUserCount(ctx context.Context, role string) (int64, error) {
return 0, nil
}
// fakeCasbinForRole 测试 CasbinService 的最小实现,覆盖 AssignRole 所需方法。
type fakeCasbinForRole struct {
roles map[string][]string // userID -> roles
hasRoleMap map[string]bool // "userID:role" -> bool
addErr error
}
func (f *fakeCasbinForRole) Enforce(ctx context.Context, sub, obj, act string) (bool, error) {
return false, nil
}
func (f *fakeCasbinForRole) EnforceForUser(ctx context.Context, userID, obj, act string) (bool, error) {
return false, nil
}
func (f *fakeCasbinForRole) AddRoleForUser(ctx context.Context, userID, role string) error {
if f.addErr != nil {
return f.addErr
}
if f.roles == nil {
f.roles = make(map[string][]string)
}
f.roles[userID] = append(f.roles[userID], role)
return nil
}
func (f *fakeCasbinForRole) DeleteRoleForUser(ctx context.Context, userID, role string) error {
return nil
}
func (f *fakeCasbinForRole) GetRolesForUser(ctx context.Context, userID string) ([]string, error) {
return f.roles[userID], nil
}
func (f *fakeCasbinForRole) HasRoleForUser(ctx context.Context, userID, role string) (bool, error) {
if f.hasRoleMap == nil {
return false, nil
}
return f.hasRoleMap[userID+":"+role], nil
}
func (f *fakeCasbinForRole) AddPolicy(ctx context.Context, role, resource, action string) error {
return nil
}
func (f *fakeCasbinForRole) RemovePolicy(ctx context.Context, role, resource, action string) error {
return nil
}
func (f *fakeCasbinForRole) LoadPolicy(ctx context.Context) error { return nil }
func (f *fakeCasbinForRole) ClearCache(ctx context.Context) error { return nil }
func (f *fakeCasbinForRole) GetPermissionsForRole(ctx context.Context, role string) ([][]string, error) {
return nil, nil
}
func (f *fakeCasbinForRole) UpdatePermissionsForRole(ctx context.Context, role string, perms [][]string) error {
return nil
}
func (f *fakeCasbinForRole) GetAllPermissions(ctx context.Context) [][]string { return nil }
// TestAssignRole_NonSuperAdminAssigningSuperAdmin 非 super_admin 分配 super_admin → 拒绝。
func TestAssignRole_NonSuperAdminAssigningSuperAdmin(t *testing.T) {
roleRepo := &fakeRoleRepo{
getRoleByName: &model.Role{Name: model.RoleSuperAdmin},
}
casbinSvc := &fakeCasbinForRole{
roles: map[string][]string{
"operator": {model.RoleAdmin}, // operator 是普通 admin不是 super_admin
},
}
svc := NewRoleService(roleRepo, casbinSvc)
err := svc.AssignRole(context.Background(), "operator", "target", model.RoleSuperAdmin)
if !errors.Is(err, apperrors.ErrCannotModifySuperAdmin) {
t.Errorf("expected ErrCannotModifySuperAdmin, got %v", err)
}
}
// TestAssignRole_SuperAdminAssigningSuperAdmin super_admin 分配 super_admin → 成功。
func TestAssignRole_SuperAdminAssigningSuperAdmin(t *testing.T) {
roleRepo := &fakeRoleRepo{
getRoleByName: &model.Role{Name: model.RoleSuperAdmin},
}
casbinSvc := &fakeCasbinForRole{
roles: map[string][]string{
"operator": {model.RoleSuperAdmin}, // operator 是 super_admin
},
}
svc := NewRoleService(roleRepo, casbinSvc)
err := svc.AssignRole(context.Background(), "operator", "target", model.RoleSuperAdmin)
if err != nil {
t.Errorf("super_admin should be able to assign super_admin, got %v", err)
}
}
// TestAssignRole_SelfModification 不能改自己的角色。
func TestAssignRole_SelfModification(t *testing.T) {
roleRepo := &fakeRoleRepo{}
casbinSvc := &fakeCasbinForRole{}
svc := NewRoleService(roleRepo, casbinSvc)
err := svc.AssignRole(context.Background(), "u1", "u1", model.RoleAdmin)
if !errors.Is(err, apperrors.ErrCannotModifyOwnRole) {
t.Errorf("expected ErrCannotModifyOwnRole, got %v", err)
}
}
// TestRemoveRole_SuperAdminRole 不能移除 super_admin 角色。
func TestRemoveRole_SuperAdminRole(t *testing.T) {
roleRepo := &fakeRoleRepo{
roles: map[string]bool{"target:" + model.RoleSuperAdmin: true},
}
casbinSvc := &fakeCasbinForRole{
hasRoleMap: map[string]bool{"target:" + model.RoleSuperAdmin: true},
}
svc := NewRoleService(roleRepo, casbinSvc)
err := svc.RemoveRole(context.Background(), "operator", "target", model.RoleSuperAdmin)
if !errors.Is(err, apperrors.ErrCannotModifySuperAdmin) {
t.Errorf("expected ErrCannotModifySuperAdmin, got %v", err)
}
}
// TestAssignRole_RoleNotFound 角色不存在 → ErrRoleNotFound。
func TestAssignRole_RoleNotFound(t *testing.T) {
roleRepo := &fakeRoleRepo{
getRoleErr: gorm.ErrRecordNotFound,
}
casbinSvc := &fakeCasbinForRole{
roles: map[string][]string{
"operator": {model.RoleSuperAdmin},
},
}
svc := NewRoleService(roleRepo, casbinSvc)
err := svc.AssignRole(context.Background(), "operator", "target", model.RoleAdmin)
if !errors.Is(err, apperrors.ErrRoleNotFound) {
t.Errorf("expected ErrRoleNotFound, got %v", err)
}
}
// 确保编译期 import 使用。
var _ = uuid.New