package auth import ( "context" "database/sql" "errors" "strings" "testing" "time" "perms-system-server/internal/loaders" "perms-system-server/internal/middleware" userModel "perms-system-server/internal/model/user" "perms-system-server/internal/response" "perms-system-server/internal/svc" "perms-system-server/internal/testutil" "perms-system-server/internal/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.org/x/crypto/bcrypt" ) func ctxWithUserId(userId int64) context.Context { return middleware.WithUserDetails(context.Background(), &loaders.UserDetails{UserId: userId}) } func insertTestUser(t *testing.T, ctx context.Context, username, password string) int64 { t.Helper() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) now := time.Now().Unix() res, err := svcCtx.SysUserModel.Insert(ctx, &userModel.SysUser{ Username: username, Password: password, Nickname: "test", Avatar: sql.NullString{}, Email: username + "@test.com", Phone: "13800000000", Remark: "", DeptId: 0, IsSuperAdmin: 2, MustChangePassword: 1, Status: 1, CreateTime: now, UpdateTime: now, }) require.NoError(t, err) id, _ := res.LastInsertId() return id } // TC-0035: 正常修改 func TestChangePassword_Success(t *testing.T) { svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() ctx := context.Background() oldPwd := "oldpass123" newPwd := "newpass456" username := testutil.UniqueId() hashed := testutil.HashPassword(oldPwd) userId := insertTestUser(t, ctx, username, hashed) t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_user`", userId) }) logic := NewChangePasswordLogic(ctxWithUserId(userId), svcCtx) err := logic.ChangePassword(&types.ChangePasswordReq{ OldPassword: oldPwd, NewPassword: newPwd, }) require.NoError(t, err) updated, err := svcCtx.SysUserModel.FindOne(ctx, userId) require.NoError(t, err) assert.NoError(t, bcrypt.CompareHashAndPassword([]byte(updated.Password), []byte(newPwd))) } // TC-0036: mustChangePassword重置 func TestChangePassword_MustChangePasswordReset(t *testing.T) { svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() ctx := context.Background() oldPwd := "oldpass123" newPwd := "newpass456" username := testutil.UniqueId() hashed := testutil.HashPassword(oldPwd) userId := insertTestUser(t, ctx, username, hashed) t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_user`", userId) }) logic := NewChangePasswordLogic(ctxWithUserId(userId), svcCtx) err := logic.ChangePassword(&types.ChangePasswordReq{ OldPassword: oldPwd, NewPassword: newPwd, }) require.NoError(t, err) updated, err := svcCtx.SysUserModel.FindOne(ctx, userId) require.NoError(t, err) assert.Equal(t, int64(2), updated.MustChangePassword) } // TC-0037: 原密码错误 func TestChangePassword_WrongOldPassword(t *testing.T) { svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() ctx := context.Background() username := testutil.UniqueId() hashed := testutil.HashPassword("realpass") userId := insertTestUser(t, ctx, username, hashed) t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_user`", userId) }) logic := NewChangePasswordLogic(ctxWithUserId(userId), svcCtx) err := logic.ChangePassword(&types.ChangePasswordReq{ OldPassword: "wrongpass", NewPassword: "newpass456", }) var codeErr *response.CodeError require.True(t, errors.As(err, &codeErr)) assert.Equal(t, 400, codeErr.Code()) assert.Equal(t, "原密码错误", codeErr.Error()) } // TC-0038: 新密码少于6字符 func TestChangePassword_NewPasswordTooShort(t *testing.T) { svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) logic := NewChangePasswordLogic(ctxWithUserId(1), svcCtx) err := logic.ChangePassword(&types.ChangePasswordReq{ OldPassword: "oldpass", NewPassword: "12345", }) var codeErr *response.CodeError require.True(t, errors.As(err, &codeErr)) assert.Equal(t, 400, codeErr.Code()) assert.Equal(t, "密码长度不能少于6个字符", codeErr.Error()) } // TC-0039: 新密码恰好6字符 func TestChangePassword_NewPasswordExactly6Chars(t *testing.T) { svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() ctx := context.Background() oldPwd := "oldpass123" newPwd := "abcdef" username := testutil.UniqueId() hashed := testutil.HashPassword(oldPwd) userId := insertTestUser(t, ctx, username, hashed) t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_user`", userId) }) logic := NewChangePasswordLogic(ctxWithUserId(userId), svcCtx) err := logic.ChangePassword(&types.ChangePasswordReq{ OldPassword: oldPwd, NewPassword: newPwd, }) require.NoError(t, err) updated, err := svcCtx.SysUserModel.FindOne(ctx, userId) require.NoError(t, err) assert.NoError(t, bcrypt.CompareHashAndPassword([]byte(updated.Password), []byte(newPwd))) } // TC-0040: 新密码空字符串 func TestChangePassword_NewPasswordEmpty(t *testing.T) { svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) logic := NewChangePasswordLogic(ctxWithUserId(1), svcCtx) err := logic.ChangePassword(&types.ChangePasswordReq{ OldPassword: "oldpass", NewPassword: "", }) var codeErr *response.CodeError require.True(t, errors.As(err, &codeErr)) assert.Equal(t, 400, codeErr.Code()) assert.Equal(t, "密码长度不能少于6个字符", codeErr.Error()) } // TC-0041: 新密码超过72字符 func TestChangePassword_NewPasswordTooLong(t *testing.T) { svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) longPwd := strings.Repeat("a", 73) logic := NewChangePasswordLogic(ctxWithUserId(1), svcCtx) err := logic.ChangePassword(&types.ChangePasswordReq{ OldPassword: "oldpass", NewPassword: longPwd, }) var codeErr *response.CodeError require.True(t, errors.As(err, &codeErr)) assert.Equal(t, 400, codeErr.Code()) assert.Equal(t, "密码长度不能超过72个字符", codeErr.Error()) } // TC-0042: 新密码恰好72字符 func TestChangePassword_NewPasswordExactly72Chars(t *testing.T) { svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() ctx := context.Background() oldPwd := "oldpass123" newPwd := strings.Repeat("b", 72) username := testutil.UniqueId() hashed := testutil.HashPassword(oldPwd) userId := insertTestUser(t, ctx, username, hashed) t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_user`", userId) }) logic := NewChangePasswordLogic(ctxWithUserId(userId), svcCtx) err := logic.ChangePassword(&types.ChangePasswordReq{ OldPassword: oldPwd, NewPassword: newPwd, }) require.NoError(t, err) updated, err := svcCtx.SysUserModel.FindOne(ctx, userId) require.NoError(t, err) assert.NoError(t, bcrypt.CompareHashAndPassword([]byte(updated.Password), []byte(newPwd))) } // TC-0043: 新旧密码相同 func TestChangePassword_SameOldAndNew(t *testing.T) { svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() ctx := context.Background() pwd := "samepass123" username := testutil.UniqueId() hashed := testutil.HashPassword(pwd) userId := insertTestUser(t, ctx, username, hashed) t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_user`", userId) }) logic := NewChangePasswordLogic(ctxWithUserId(userId), svcCtx) err := logic.ChangePassword(&types.ChangePasswordReq{ OldPassword: pwd, NewPassword: pwd, }) var codeErr *response.CodeError require.True(t, errors.As(err, &codeErr)) assert.Equal(t, 400, codeErr.Code()) assert.Equal(t, "新密码不能与原密码相同", codeErr.Error()) } // TC-0044: 用户不存在 func TestChangePassword_UserNotFound(t *testing.T) { svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) logic := NewChangePasswordLogic(ctxWithUserId(99999999), svcCtx) err := logic.ChangePassword(&types.ChangePasswordReq{ OldPassword: "oldpass", NewPassword: "newpass456", }) var codeErr *response.CodeError require.True(t, errors.As(err, &codeErr)) assert.Equal(t, 404, codeErr.Code()) assert.Equal(t, "用户不存在", codeErr.Error()) }