| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150 |
- //go:build unit
- package service
- import (
- "context"
- "testing"
- "github.com/Wei-Shaw/sub2api/internal/pkg/pagination"
- "github.com/stretchr/testify/require"
- )
- func TestAdminService_CreateUser_WithAdminRole(t *testing.T) {
- repo := &userRepoStub{nextID: 30, usersByID: map[int64]*User{1: {ID: 1, Role: RoleSuperAdmin, Status: StatusActive}}}
- svc := &adminServiceImpl{userRepo: repo}
- user, err := svc.CreateUser(context.Background(), &CreateUserInput{
- Email: "admin@test.com",
- Password: "strong-pass",
- Role: RoleAdmin,
- ActorAdminID: 1,
- })
- require.NoError(t, err)
- require.Equal(t, RoleAdmin, user.Role)
- }
- func TestAdminService_CreateUser_DefaultsToUserRole(t *testing.T) {
- repo := &userRepoStub{nextID: 31}
- svc := &adminServiceImpl{userRepo: repo}
- user, err := svc.CreateUser(context.Background(), &CreateUserInput{
- Email: "plain@test.com",
- Password: "strong-pass",
- })
- require.NoError(t, err)
- require.Equal(t, RoleUser, user.Role)
- }
- func TestAdminService_CreateUser_InvalidRoleRejected(t *testing.T) {
- repo := &userRepoStub{nextID: 32}
- svc := &adminServiceImpl{userRepo: repo}
- _, err := svc.CreateUser(context.Background(), &CreateUserInput{
- Email: "bad@test.com",
- Password: "strong-pass",
- Role: "superuser",
- })
- require.Error(t, err)
- require.Empty(t, repo.created, "非法角色不应写入用户")
- }
- func TestAdminService_UpdateUser_PromoteToAdmin(t *testing.T) {
- base := &userRepoStub{user: &User{ID: 42, Email: "u@example.com", Role: RoleUser}}
- base.usersByID = map[int64]*User{1: {ID: 1, Role: RoleSuperAdmin, Status: StatusActive}, 42: base.user}
- repo := &rpmUserRepoStub{userRepoStub: base}
- invalidator := &authCacheInvalidatorStub{}
- svc := &adminServiceImpl{
- userRepo: repo,
- redeemCodeRepo: &redeemRepoStub{},
- authCacheInvalidator: invalidator,
- }
- updated, err := svc.UpdateUser(context.Background(), 42, &UpdateUserInput{ActorAdminID: 1, Role: RoleAdmin})
- require.NoError(t, err)
- require.Equal(t, RoleAdmin, updated.Role)
- require.Equal(t, []int64{42}, invalidator.userIDs, "角色变更应失效认证缓存")
- }
- func TestAdminService_UpdateUser_RoleOmittedKeepsExisting(t *testing.T) {
- base := &userRepoStub{user: &User{ID: 42, Email: "u@example.com", Role: RoleAdmin}}
- base.usersByID = map[int64]*User{1: {ID: 1, Role: RoleSuperAdmin, Status: StatusActive}, 42: base.user}
- repo := &rpmUserRepoStub{userRepoStub: base}
- svc := &adminServiceImpl{userRepo: repo, redeemCodeRepo: &redeemRepoStub{}}
- newName := "renamed"
- updated, err := svc.UpdateUser(context.Background(), 42, &UpdateUserInput{ActorAdminID: 1, Username: &newName})
- require.NoError(t, err)
- require.Equal(t, RoleAdmin, updated.Role, "未提供 role 时不应改变现有角色")
- }
- func TestAdminService_UpdateUser_InvalidRoleRejected(t *testing.T) {
- base := &userRepoStub{user: &User{ID: 42, Email: "u@example.com", Role: RoleUser}}
- base.usersByID = map[int64]*User{1: {ID: 1, Role: RoleSuperAdmin, Status: StatusActive}, 42: base.user}
- repo := &rpmUserRepoStub{userRepoStub: base}
- svc := &adminServiceImpl{userRepo: repo, redeemCodeRepo: &redeemRepoStub{}}
- _, err := svc.UpdateUser(context.Background(), 42, &UpdateUserInput{ActorAdminID: 1, Role: "root"})
- require.Error(t, err)
- require.Nil(t, repo.lastUpdated, "非法角色不应触发持久化")
- }
- // roleGuardUserRepoStub 在 rpmUserRepoStub 之上提供可控的管理员计数,
- // 用于测试"最后一个管理员不可降级"守卫。
- type roleGuardUserRepoStub struct {
- *rpmUserRepoStub
- adminTotal int64
- listCalls int
- }
- func (s *roleGuardUserRepoStub) ListWithFilters(_ context.Context, _ pagination.PaginationParams, _ UserListFilters) ([]User, *pagination.PaginationResult, error) {
- s.listCalls++
- return nil, &pagination.PaginationResult{Total: s.adminTotal}, nil
- }
- func TestAdminService_UpdateUser_DemoteLastSuperAdminRejected(t *testing.T) {
- base := &userRepoStub{user: &User{ID: 42, Email: "a@example.com", Role: RoleSuperAdmin}}
- base.usersByID = map[int64]*User{1: {ID: 1, Role: RoleSuperAdmin, Status: StatusActive}, 42: base.user}
- repo := &roleGuardUserRepoStub{rpmUserRepoStub: &rpmUserRepoStub{userRepoStub: base}, adminTotal: 1}
- svc := &adminServiceImpl{userRepo: repo, redeemCodeRepo: &redeemRepoStub{}}
- _, err := svc.UpdateUser(context.Background(), 42, &UpdateUserInput{ActorAdminID: 1, Role: RoleUser})
- require.Error(t, err)
- require.Contains(t, err.Error(), "最后一个超级管理员")
- require.Nil(t, repo.lastUpdated, "最后一个管理员不应被降级持久化")
- require.Equal(t, 1, repo.listCalls, "降级路径应触发管理员计数")
- }
- func TestAdminService_UpdateUser_DemoteAdminAllowedWhenOthersExist(t *testing.T) {
- base := &userRepoStub{user: &User{ID: 42, Email: "a@example.com", Role: RoleAdmin}}
- base.usersByID = map[int64]*User{1: {ID: 1, Role: RoleSuperAdmin, Status: StatusActive}, 42: base.user}
- repo := &roleGuardUserRepoStub{rpmUserRepoStub: &rpmUserRepoStub{userRepoStub: base}, adminTotal: 2}
- invalidator := &authCacheInvalidatorStub{}
- svc := &adminServiceImpl{
- userRepo: repo,
- redeemCodeRepo: &redeemRepoStub{},
- authCacheInvalidator: invalidator,
- }
- updated, err := svc.UpdateUser(context.Background(), 42, &UpdateUserInput{ActorAdminID: 1, Role: RoleUser})
- require.NoError(t, err)
- require.Equal(t, RoleUser, updated.Role)
- require.NotNil(t, repo.lastUpdated)
- require.Equal(t, RoleUser, repo.lastUpdated.Role, "存在其他管理员时允许降级")
- }
- func TestAdminService_UpdateUser_PromoteDoesNotCountAdmins(t *testing.T) {
- base := &userRepoStub{user: &User{ID: 42, Email: "u@example.com", Role: RoleUser}}
- base.usersByID = map[int64]*User{1: {ID: 1, Role: RoleSuperAdmin, Status: StatusActive}, 42: base.user}
- repo := &roleGuardUserRepoStub{rpmUserRepoStub: &rpmUserRepoStub{userRepoStub: base}, adminTotal: 1}
- svc := &adminServiceImpl{
- userRepo: repo,
- redeemCodeRepo: &redeemRepoStub{},
- authCacheInvalidator: &authCacheInvalidatorStub{},
- }
- updated, err := svc.UpdateUser(context.Background(), 42, &UpdateUserInput{ActorAdminID: 1, Role: RoleAdmin})
- require.NoError(t, err)
- require.Equal(t, RoleAdmin, updated.Role)
- require.Equal(t, 0, repo.listCalls, "升级路径不应触发管理员计数")
- }
|