admin_service_role_test.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. //go:build unit
  2. package service
  3. import (
  4. "context"
  5. "testing"
  6. "github.com/Wei-Shaw/sub2api/internal/pkg/pagination"
  7. "github.com/stretchr/testify/require"
  8. )
  9. func TestAdminService_CreateUser_WithAdminRole(t *testing.T) {
  10. repo := &userRepoStub{nextID: 30, usersByID: map[int64]*User{1: {ID: 1, Role: RoleSuperAdmin, Status: StatusActive}}}
  11. svc := &adminServiceImpl{userRepo: repo}
  12. user, err := svc.CreateUser(context.Background(), &CreateUserInput{
  13. Email: "admin@test.com",
  14. Password: "strong-pass",
  15. Role: RoleAdmin,
  16. ActorAdminID: 1,
  17. })
  18. require.NoError(t, err)
  19. require.Equal(t, RoleAdmin, user.Role)
  20. }
  21. func TestAdminService_CreateUser_DefaultsToUserRole(t *testing.T) {
  22. repo := &userRepoStub{nextID: 31}
  23. svc := &adminServiceImpl{userRepo: repo}
  24. user, err := svc.CreateUser(context.Background(), &CreateUserInput{
  25. Email: "plain@test.com",
  26. Password: "strong-pass",
  27. })
  28. require.NoError(t, err)
  29. require.Equal(t, RoleUser, user.Role)
  30. }
  31. func TestAdminService_CreateUser_InvalidRoleRejected(t *testing.T) {
  32. repo := &userRepoStub{nextID: 32}
  33. svc := &adminServiceImpl{userRepo: repo}
  34. _, err := svc.CreateUser(context.Background(), &CreateUserInput{
  35. Email: "bad@test.com",
  36. Password: "strong-pass",
  37. Role: "superuser",
  38. })
  39. require.Error(t, err)
  40. require.Empty(t, repo.created, "非法角色不应写入用户")
  41. }
  42. func TestAdminService_UpdateUser_PromoteToAdmin(t *testing.T) {
  43. base := &userRepoStub{user: &User{ID: 42, Email: "u@example.com", Role: RoleUser}}
  44. base.usersByID = map[int64]*User{1: {ID: 1, Role: RoleSuperAdmin, Status: StatusActive}, 42: base.user}
  45. repo := &rpmUserRepoStub{userRepoStub: base}
  46. invalidator := &authCacheInvalidatorStub{}
  47. svc := &adminServiceImpl{
  48. userRepo: repo,
  49. redeemCodeRepo: &redeemRepoStub{},
  50. authCacheInvalidator: invalidator,
  51. }
  52. updated, err := svc.UpdateUser(context.Background(), 42, &UpdateUserInput{ActorAdminID: 1, Role: RoleAdmin})
  53. require.NoError(t, err)
  54. require.Equal(t, RoleAdmin, updated.Role)
  55. require.Equal(t, []int64{42}, invalidator.userIDs, "角色变更应失效认证缓存")
  56. }
  57. func TestAdminService_UpdateUser_RoleOmittedKeepsExisting(t *testing.T) {
  58. base := &userRepoStub{user: &User{ID: 42, Email: "u@example.com", Role: RoleAdmin}}
  59. base.usersByID = map[int64]*User{1: {ID: 1, Role: RoleSuperAdmin, Status: StatusActive}, 42: base.user}
  60. repo := &rpmUserRepoStub{userRepoStub: base}
  61. svc := &adminServiceImpl{userRepo: repo, redeemCodeRepo: &redeemRepoStub{}}
  62. newName := "renamed"
  63. updated, err := svc.UpdateUser(context.Background(), 42, &UpdateUserInput{ActorAdminID: 1, Username: &newName})
  64. require.NoError(t, err)
  65. require.Equal(t, RoleAdmin, updated.Role, "未提供 role 时不应改变现有角色")
  66. }
  67. func TestAdminService_UpdateUser_InvalidRoleRejected(t *testing.T) {
  68. base := &userRepoStub{user: &User{ID: 42, Email: "u@example.com", Role: RoleUser}}
  69. base.usersByID = map[int64]*User{1: {ID: 1, Role: RoleSuperAdmin, Status: StatusActive}, 42: base.user}
  70. repo := &rpmUserRepoStub{userRepoStub: base}
  71. svc := &adminServiceImpl{userRepo: repo, redeemCodeRepo: &redeemRepoStub{}}
  72. _, err := svc.UpdateUser(context.Background(), 42, &UpdateUserInput{ActorAdminID: 1, Role: "root"})
  73. require.Error(t, err)
  74. require.Nil(t, repo.lastUpdated, "非法角色不应触发持久化")
  75. }
  76. // roleGuardUserRepoStub 在 rpmUserRepoStub 之上提供可控的管理员计数,
  77. // 用于测试"最后一个管理员不可降级"守卫。
  78. type roleGuardUserRepoStub struct {
  79. *rpmUserRepoStub
  80. adminTotal int64
  81. listCalls int
  82. }
  83. func (s *roleGuardUserRepoStub) ListWithFilters(_ context.Context, _ pagination.PaginationParams, _ UserListFilters) ([]User, *pagination.PaginationResult, error) {
  84. s.listCalls++
  85. return nil, &pagination.PaginationResult{Total: s.adminTotal}, nil
  86. }
  87. func TestAdminService_UpdateUser_DemoteLastSuperAdminRejected(t *testing.T) {
  88. base := &userRepoStub{user: &User{ID: 42, Email: "a@example.com", Role: RoleSuperAdmin}}
  89. base.usersByID = map[int64]*User{1: {ID: 1, Role: RoleSuperAdmin, Status: StatusActive}, 42: base.user}
  90. repo := &roleGuardUserRepoStub{rpmUserRepoStub: &rpmUserRepoStub{userRepoStub: base}, adminTotal: 1}
  91. svc := &adminServiceImpl{userRepo: repo, redeemCodeRepo: &redeemRepoStub{}}
  92. _, err := svc.UpdateUser(context.Background(), 42, &UpdateUserInput{ActorAdminID: 1, Role: RoleUser})
  93. require.Error(t, err)
  94. require.Contains(t, err.Error(), "最后一个超级管理员")
  95. require.Nil(t, repo.lastUpdated, "最后一个管理员不应被降级持久化")
  96. require.Equal(t, 1, repo.listCalls, "降级路径应触发管理员计数")
  97. }
  98. func TestAdminService_UpdateUser_DemoteAdminAllowedWhenOthersExist(t *testing.T) {
  99. base := &userRepoStub{user: &User{ID: 42, Email: "a@example.com", Role: RoleAdmin}}
  100. base.usersByID = map[int64]*User{1: {ID: 1, Role: RoleSuperAdmin, Status: StatusActive}, 42: base.user}
  101. repo := &roleGuardUserRepoStub{rpmUserRepoStub: &rpmUserRepoStub{userRepoStub: base}, adminTotal: 2}
  102. invalidator := &authCacheInvalidatorStub{}
  103. svc := &adminServiceImpl{
  104. userRepo: repo,
  105. redeemCodeRepo: &redeemRepoStub{},
  106. authCacheInvalidator: invalidator,
  107. }
  108. updated, err := svc.UpdateUser(context.Background(), 42, &UpdateUserInput{ActorAdminID: 1, Role: RoleUser})
  109. require.NoError(t, err)
  110. require.Equal(t, RoleUser, updated.Role)
  111. require.NotNil(t, repo.lastUpdated)
  112. require.Equal(t, RoleUser, repo.lastUpdated.Role, "存在其他管理员时允许降级")
  113. }
  114. func TestAdminService_UpdateUser_PromoteDoesNotCountAdmins(t *testing.T) {
  115. base := &userRepoStub{user: &User{ID: 42, Email: "u@example.com", Role: RoleUser}}
  116. base.usersByID = map[int64]*User{1: {ID: 1, Role: RoleSuperAdmin, Status: StatusActive}, 42: base.user}
  117. repo := &roleGuardUserRepoStub{rpmUserRepoStub: &rpmUserRepoStub{userRepoStub: base}, adminTotal: 1}
  118. svc := &adminServiceImpl{
  119. userRepo: repo,
  120. redeemCodeRepo: &redeemRepoStub{},
  121. authCacheInvalidator: &authCacheInvalidatorStub{},
  122. }
  123. updated, err := svc.UpdateUser(context.Background(), 42, &UpdateUserInput{ActorAdminID: 1, Role: RoleAdmin})
  124. require.NoError(t, err)
  125. require.Equal(t, RoleAdmin, updated.Role)
  126. require.Equal(t, 0, repo.listCalls, "升级路径不应触发管理员计数")
  127. }