changePasswordLogic_test.go 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. package auth
  2. import (
  3. "context"
  4. "database/sql"
  5. "errors"
  6. "strings"
  7. "testing"
  8. "time"
  9. "perms-system-server/internal/loaders"
  10. "perms-system-server/internal/middleware"
  11. userModel "perms-system-server/internal/model/user"
  12. "perms-system-server/internal/response"
  13. "perms-system-server/internal/svc"
  14. "perms-system-server/internal/testutil"
  15. "perms-system-server/internal/types"
  16. "github.com/stretchr/testify/assert"
  17. "github.com/stretchr/testify/require"
  18. "golang.org/x/crypto/bcrypt"
  19. )
  20. func ctxWithUserId(userId int64) context.Context {
  21. return middleware.WithUserDetails(context.Background(), &loaders.UserDetails{UserId: userId})
  22. }
  23. func insertTestUser(t *testing.T, ctx context.Context, username, password string) int64 {
  24. t.Helper()
  25. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  26. now := time.Now().Unix()
  27. res, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{
  28. Username: username,
  29. Password: password,
  30. Nickname: "test",
  31. Avatar: sql.NullString{},
  32. Email: username + "@test.com",
  33. Phone: "13800000000",
  34. Remark: "",
  35. DeptId: 0,
  36. IsSuperAdmin: 2,
  37. MustChangePassword: 1,
  38. Status: 1,
  39. CreateTime: now,
  40. UpdateTime: now,
  41. })
  42. require.NoError(t, err)
  43. id, _ := res.LastInsertId()
  44. return id
  45. }
  46. // TC-0050: 正常修改
  47. func TestChangePassword_Success(t *testing.T) {
  48. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  49. conn := testutil.GetTestSqlConn()
  50. ctx := context.Background()
  51. oldPwd := "Oldpass123"
  52. newPwd := "Newpass456"
  53. username := testutil.UniqueId()
  54. hashed := testutil.HashPassword(oldPwd)
  55. userId := insertTestUser(t, ctx, username, hashed)
  56. t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_user`", userId) })
  57. logic := NewChangePasswordLogic(ctxWithUserId(userId), svcCtx)
  58. err := logic.ChangePassword(&types.ChangePasswordReq{
  59. OldPassword: oldPwd,
  60. NewPassword: newPwd,
  61. })
  62. require.NoError(t, err)
  63. updated, err := svcCtx.SysUserModel.FindOne(ctx, userId)
  64. require.NoError(t, err)
  65. assert.NoError(t, bcrypt.CompareHashAndPassword([]byte(updated.Password), []byte(newPwd)))
  66. }
  67. // TC-0051: mustChangePassword重置
  68. func TestChangePassword_MustChangePasswordReset(t *testing.T) {
  69. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  70. conn := testutil.GetTestSqlConn()
  71. ctx := context.Background()
  72. oldPwd := "Oldpass123"
  73. newPwd := "Newpass456"
  74. username := testutil.UniqueId()
  75. hashed := testutil.HashPassword(oldPwd)
  76. userId := insertTestUser(t, ctx, username, hashed)
  77. t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_user`", userId) })
  78. logic := NewChangePasswordLogic(ctxWithUserId(userId), svcCtx)
  79. err := logic.ChangePassword(&types.ChangePasswordReq{
  80. OldPassword: oldPwd,
  81. NewPassword: newPwd,
  82. })
  83. require.NoError(t, err)
  84. updated, err := svcCtx.SysUserModel.FindOne(ctx, userId)
  85. require.NoError(t, err)
  86. assert.Equal(t, int64(2), updated.MustChangePassword)
  87. }
  88. // TC-0052: 原密码错误
  89. func TestChangePassword_WrongOldPassword(t *testing.T) {
  90. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  91. conn := testutil.GetTestSqlConn()
  92. ctx := context.Background()
  93. username := testutil.UniqueId()
  94. hashed := testutil.HashPassword("Realpass1")
  95. userId := insertTestUser(t, ctx, username, hashed)
  96. t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_user`", userId) })
  97. logic := NewChangePasswordLogic(ctxWithUserId(userId), svcCtx)
  98. err := logic.ChangePassword(&types.ChangePasswordReq{
  99. OldPassword: "Wrongpass1",
  100. NewPassword: "Newpass456",
  101. })
  102. var codeErr *response.CodeError
  103. require.True(t, errors.As(err, &codeErr))
  104. assert.Equal(t, 400, codeErr.Code())
  105. assert.Equal(t, "原密码错误", codeErr.Error())
  106. }
  107. // TC-0053: 新密码少于8字符
  108. func TestChangePassword_NewPasswordTooShort(t *testing.T) {
  109. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  110. logic := NewChangePasswordLogic(ctxWithUserId(1), svcCtx)
  111. err := logic.ChangePassword(&types.ChangePasswordReq{
  112. OldPassword: "oldpass",
  113. NewPassword: "Pas1234",
  114. })
  115. var codeErr *response.CodeError
  116. require.True(t, errors.As(err, &codeErr))
  117. assert.Equal(t, 400, codeErr.Code())
  118. assert.Equal(t, "密码长度不能少于8个字符", codeErr.Error())
  119. }
  120. // TC-0054: 新密码恰好8字符(含大小写+数字)
  121. func TestChangePassword_NewPasswordExactly8Chars(t *testing.T) {
  122. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  123. conn := testutil.GetTestSqlConn()
  124. ctx := context.Background()
  125. oldPwd := "Oldpass123"
  126. newPwd := "Abcdef1x"
  127. username := testutil.UniqueId()
  128. hashed := testutil.HashPassword(oldPwd)
  129. userId := insertTestUser(t, ctx, username, hashed)
  130. t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_user`", userId) })
  131. logic := NewChangePasswordLogic(ctxWithUserId(userId), svcCtx)
  132. err := logic.ChangePassword(&types.ChangePasswordReq{
  133. OldPassword: oldPwd,
  134. NewPassword: newPwd,
  135. })
  136. require.NoError(t, err)
  137. updated, err := svcCtx.SysUserModel.FindOne(ctx, userId)
  138. require.NoError(t, err)
  139. assert.NoError(t, bcrypt.CompareHashAndPassword([]byte(updated.Password), []byte(newPwd)))
  140. }
  141. // TC-0055: 新密码空字符串
  142. func TestChangePassword_NewPasswordEmpty(t *testing.T) {
  143. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  144. logic := NewChangePasswordLogic(ctxWithUserId(1), svcCtx)
  145. err := logic.ChangePassword(&types.ChangePasswordReq{
  146. OldPassword: "oldpass",
  147. NewPassword: "",
  148. })
  149. var codeErr *response.CodeError
  150. require.True(t, errors.As(err, &codeErr))
  151. assert.Equal(t, 400, codeErr.Code())
  152. assert.Equal(t, "密码长度不能少于8个字符", codeErr.Error())
  153. }
  154. // TC-0056: 新密码超过72字符
  155. func TestChangePassword_NewPasswordTooLong(t *testing.T) {
  156. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  157. longPwd := "A" + strings.Repeat("a", 71) + "1"
  158. logic := NewChangePasswordLogic(ctxWithUserId(1), svcCtx)
  159. err := logic.ChangePassword(&types.ChangePasswordReq{
  160. OldPassword: "oldpass",
  161. NewPassword: longPwd,
  162. })
  163. var codeErr *response.CodeError
  164. require.True(t, errors.As(err, &codeErr))
  165. assert.Equal(t, 400, codeErr.Code())
  166. assert.Equal(t, "密码长度不能超过72个字符", codeErr.Error())
  167. }
  168. // TC-0057: 新密码恰好72字符
  169. func TestChangePassword_NewPasswordExactly72Chars(t *testing.T) {
  170. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  171. conn := testutil.GetTestSqlConn()
  172. ctx := context.Background()
  173. oldPwd := "Oldpass123"
  174. newPwd := "B" + strings.Repeat("b", 70) + "1"
  175. username := testutil.UniqueId()
  176. hashed := testutil.HashPassword(oldPwd)
  177. userId := insertTestUser(t, ctx, username, hashed)
  178. t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_user`", userId) })
  179. logic := NewChangePasswordLogic(ctxWithUserId(userId), svcCtx)
  180. err := logic.ChangePassword(&types.ChangePasswordReq{
  181. OldPassword: oldPwd,
  182. NewPassword: newPwd,
  183. })
  184. require.NoError(t, err)
  185. updated, err := svcCtx.SysUserModel.FindOne(ctx, userId)
  186. require.NoError(t, err)
  187. assert.NoError(t, bcrypt.CompareHashAndPassword([]byte(updated.Password), []byte(newPwd)))
  188. }
  189. // TC-0058: 新旧密码相同
  190. func TestChangePassword_SameOldAndNew(t *testing.T) {
  191. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  192. conn := testutil.GetTestSqlConn()
  193. ctx := context.Background()
  194. pwd := "Samepass123"
  195. username := testutil.UniqueId()
  196. hashed := testutil.HashPassword(pwd)
  197. userId := insertTestUser(t, ctx, username, hashed)
  198. t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_user`", userId) })
  199. logic := NewChangePasswordLogic(ctxWithUserId(userId), svcCtx)
  200. err := logic.ChangePassword(&types.ChangePasswordReq{
  201. OldPassword: pwd,
  202. NewPassword: pwd,
  203. })
  204. var codeErr *response.CodeError
  205. require.True(t, errors.As(err, &codeErr))
  206. assert.Equal(t, 400, codeErr.Code())
  207. assert.Equal(t, "新密码不能与原密码相同", codeErr.Error())
  208. }
  209. // TC-0059: 用户不存在
  210. func TestChangePassword_UserNotFound(t *testing.T) {
  211. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  212. logic := NewChangePasswordLogic(ctxWithUserId(99999999), svcCtx)
  213. err := logic.ChangePassword(&types.ChangePasswordReq{
  214. OldPassword: "Oldpass123",
  215. NewPassword: "Newpass456",
  216. })
  217. var codeErr *response.CodeError
  218. require.True(t, errors.As(err, &codeErr))
  219. assert.Equal(t, 404, codeErr.Code())
  220. assert.Equal(t, "用户不存在", codeErr.Error())
  221. }