package user import ( "context" "errors" "testing" "perms-system-server/internal/consts" "perms-system-server/internal/loaders" "perms-system-server/internal/response" "perms-system-server/internal/svc" "perms-system-server/internal/testutil" "perms-system-server/internal/testutil/ctxhelper" "perms-system-server/internal/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // TC-0112: 正常更新 func TestUpdateUser_Success(t *testing.T) { ctx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() username := testutil.UniqueId() userId := insertTestUser(t, ctx, username, testutil.HashPassword("pass")) t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_user`", userId) }) logic := NewUpdateUserLogic(ctx, svcCtx) err := logic.UpdateUser(&types.UpdateUserReq{ Id: userId, Nickname: strPtr("新昵称"), Email: strPtr("new@example.com"), Phone: strPtr("13900139000"), Remark: strPtr("更新备注"), DeptId: int64Ptr(5), }) require.NoError(t, err) user, err := svcCtx.SysUserModel.FindOne(ctx, userId) require.NoError(t, err) assert.Equal(t, "新昵称", user.Nickname) assert.Equal(t, "new@example.com", user.Email) assert.Equal(t, "13900139000", user.Phone) assert.Equal(t, "更新备注", user.Remark) assert.Equal(t, int64(5), user.DeptId) } // TC-0113: 不存在 func TestUpdateUser_NotFound(t *testing.T) { ctx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) logic := NewUpdateUserLogic(ctx, svcCtx) err := logic.UpdateUser(&types.UpdateUserReq{ Id: 999999999, Nickname: strPtr("ghost"), }) require.Error(t, err) var codeErr *response.CodeError require.True(t, errors.As(err, &codeErr)) assert.Equal(t, 404, codeErr.Code()) assert.Equal(t, "用户不存在", codeErr.Error()) } // TC-0114: 仅传id func TestUpdateUser_OnlyId_NothingChanges(t *testing.T) { ctx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() username := testutil.UniqueId() userId := insertTestUser(t, ctx, username, testutil.HashPassword("pass")) t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_user`", userId) }) before, err := svcCtx.SysUserModel.FindOne(ctx, userId) require.NoError(t, err) logic := NewUpdateUserLogic(ctx, svcCtx) err = logic.UpdateUser(&types.UpdateUserReq{Id: userId}) require.NoError(t, err) after, err := svcCtx.SysUserModel.FindOne(ctx, userId) require.NoError(t, err) assert.Equal(t, before.Nickname, after.Nickname) assert.Equal(t, before.Email, after.Email) assert.Equal(t, before.Phone, after.Phone) assert.Equal(t, before.DeptId, after.DeptId) } // TC-0115: 清空nickname func TestUpdateUser_ClearNickname(t *testing.T) { ctx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() username := testutil.UniqueId() userId := insertTestUser(t, ctx, username, testutil.HashPassword("pass")) t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_user`", userId) }) logic := NewUpdateUserLogic(ctx, svcCtx) err := logic.UpdateUser(&types.UpdateUserReq{ Id: userId, Nickname: strPtr(""), }) require.NoError(t, err) user, err := svcCtx.SysUserModel.FindOne(ctx, userId) require.NoError(t, err) assert.Equal(t, "", user.Nickname) } // TC-0116: 清空email func TestUpdateUser_ClearEmail(t *testing.T) { ctx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() username := testutil.UniqueId() userId := insertTestUser(t, ctx, username, testutil.HashPassword("pass")) t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_user`", userId) }) logic := NewUpdateUserLogic(ctx, svcCtx) err := logic.UpdateUser(&types.UpdateUserReq{ Id: userId, Email: strPtr(""), }) require.NoError(t, err) user, err := svcCtx.SysUserModel.FindOne(ctx, userId) require.NoError(t, err) assert.Equal(t, "", user.Email) } // TC-0118: 非法email格式 func TestUpdateUser_InvalidEmail(t *testing.T) { ctx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() username := testutil.UniqueId() userId := insertTestUser(t, ctx, username, testutil.HashPassword("pass")) t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_user`", userId) }) logic := NewUpdateUserLogic(ctx, svcCtx) err := logic.UpdateUser(&types.UpdateUserReq{ Id: userId, Email: strPtr("bad-email"), }) require.Error(t, err) var codeErr *response.CodeError require.True(t, errors.As(err, &codeErr)) assert.Equal(t, 400, codeErr.Code()) assert.Equal(t, "邮箱格式不正确", codeErr.Error()) } // TC-0122: DeptId设为0(取消部门) func TestUpdateUser_DeptIdZero_Clear(t *testing.T) { ctx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() username := testutil.UniqueId() userId := insertTestUser(t, ctx, username, testutil.HashPassword("pass")) t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_user`", userId) }) logic := NewUpdateUserLogic(ctx, svcCtx) err := logic.UpdateUser(&types.UpdateUserReq{ Id: userId, DeptId: int64Ptr(5), }) require.NoError(t, err) err = logic.UpdateUser(&types.UpdateUserReq{ Id: userId, DeptId: int64Ptr(0), }) require.NoError(t, err) user, err := svcCtx.SysUserModel.FindOne(ctx, userId) require.NoError(t, err) assert.Equal(t, int64(0), user.DeptId) } // TC-0123: DeptId设为正值 func TestUpdateUser_DeptIdSet(t *testing.T) { ctx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() username := testutil.UniqueId() userId := insertTestUser(t, ctx, username, testutil.HashPassword("pass")) t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_user`", userId) }) logic := NewUpdateUserLogic(ctx, svcCtx) err := logic.UpdateUser(&types.UpdateUserReq{ Id: userId, DeptId: int64Ptr(5), }) require.NoError(t, err) user, err := svcCtx.SysUserModel.FindOne(ctx, userId) require.NoError(t, err) assert.Equal(t, int64(5), user.DeptId) } // TC-0124: DeptId不传(nil) func TestUpdateUser_NilDeptId_Unchanged(t *testing.T) { ctx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() username := testutil.UniqueId() userId := insertTestUser(t, ctx, username, testutil.HashPassword("pass")) t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_user`", userId) }) logic := NewUpdateUserLogic(ctx, svcCtx) err := logic.UpdateUser(&types.UpdateUserReq{ Id: userId, DeptId: int64Ptr(7), }) require.NoError(t, err) err = logic.UpdateUser(&types.UpdateUserReq{ Id: userId, Nickname: strPtr("changed"), }) require.NoError(t, err) user, err := svcCtx.SysUserModel.FindOne(ctx, userId) require.NoError(t, err) assert.Equal(t, int64(7), user.DeptId) assert.Equal(t, "changed", user.Nickname) } // TC-0117: 清空remark func TestUpdateUser_ClearRemark(t *testing.T) { ctx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() username := testutil.UniqueId() userId := insertTestUser(t, ctx, username, testutil.HashPassword("pass")) t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_user`", userId) }) logic := NewUpdateUserLogic(ctx, svcCtx) err := logic.UpdateUser(&types.UpdateUserReq{ Id: userId, Remark: strPtr("some remark"), }) require.NoError(t, err) err = logic.UpdateUser(&types.UpdateUserReq{ Id: userId, Remark: strPtr(""), }) require.NoError(t, err) user, err := svcCtx.SysUserModel.FindOne(ctx, userId) require.NoError(t, err) assert.Equal(t, "", user.Remark) } // TC-0120: 合法phone func TestUpdateUser_ValidInternationalPhone(t *testing.T) { ctx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() username := testutil.UniqueId() userId := insertTestUser(t, ctx, username, testutil.HashPassword("pass")) t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_user`", userId) }) logic := NewUpdateUserLogic(ctx, svcCtx) err := logic.UpdateUser(&types.UpdateUserReq{ Id: userId, Phone: strPtr("+8613800138000"), }) require.NoError(t, err) user, err := svcCtx.SysUserModel.FindOne(ctx, userId) require.NoError(t, err) assert.Equal(t, "+8613800138000", user.Phone) } // TC-0119: 非法phone格式 func TestUpdateUser_InvalidPhone(t *testing.T) { ctx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() username := testutil.UniqueId() userId := insertTestUser(t, ctx, username, testutil.HashPassword("pass")) t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_user`", userId) }) logic := NewUpdateUserLogic(ctx, svcCtx) err := logic.UpdateUser(&types.UpdateUserReq{ Id: userId, Phone: strPtr("12345"), }) require.Error(t, err) var codeErr *response.CodeError require.True(t, errors.As(err, &codeErr)) assert.Equal(t, 400, codeErr.Code()) assert.Equal(t, "手机号格式不正确", codeErr.Error()) } // TC-0121: 不传email(nil) func TestUpdateUser_NilEmail_Unchanged(t *testing.T) { ctx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() username := testutil.UniqueId() userId := insertTestUser(t, ctx, username, testutil.HashPassword("pass")) t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_user`", userId) }) before, err := svcCtx.SysUserModel.FindOne(ctx, userId) require.NoError(t, err) originalEmail := before.Email logic := NewUpdateUserLogic(ctx, svcCtx) err = logic.UpdateUser(&types.UpdateUserReq{ Id: userId, Nickname: strPtr("changed-nick"), }) require.NoError(t, err) after, err := svcCtx.SysUserModel.FindOne(ctx, userId) require.NoError(t, err) assert.Equal(t, originalEmail, after.Email) assert.Equal(t, "changed-nick", after.Nickname) } // TC-0491: 非本人非超管修改拒绝 func TestUpdateUser_NonSelfNonSuperAdminRejected(t *testing.T) { ctx := ctxhelper.MemberCtx("test_product") svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) logic := NewUpdateUserLogic(ctx, svcCtx) err := logic.UpdateUser(&types.UpdateUserReq{Id: 999, Nickname: strPtr("hacked")}) require.Error(t, err) var ce *response.CodeError require.True(t, errors.As(err, &ce)) assert.Equal(t, 403, ce.Code()) assert.Contains(t, ce.Error(), "仅允许修改自己的信息或超管操作") } // TC-0511: updateUser自己修改DeptId被拒绝 func TestUpdateUser_SelfEditDeptIdRejected(t *testing.T) { ctx := ctxhelper.CustomCtx(&loaders.UserDetails{ UserId: 100, Username: "self_user", Status: consts.StatusEnabled, }) svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) logic := NewUpdateUserLogic(ctx, svcCtx) err := logic.UpdateUser(&types.UpdateUserReq{Id: 100, DeptId: int64Ptr(5)}) require.Error(t, err) var ce *response.CodeError require.True(t, errors.As(err, &ce)) assert.Equal(t, 403, ce.Code()) assert.Equal(t, "不允许修改自己的部门和状态", ce.Error()) } // TC-0512: updateUser自己修改Status被拒绝 func TestUpdateUser_SelfEditStatusRejected(t *testing.T) { ctx := ctxhelper.CustomCtx(&loaders.UserDetails{ UserId: 100, Username: "self_user", Status: consts.StatusEnabled, }) svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) logic := NewUpdateUserLogic(ctx, svcCtx) err := logic.UpdateUser(&types.UpdateUserReq{Id: 100, Status: 2}) require.Error(t, err) var ce *response.CodeError require.True(t, errors.As(err, &ce)) assert.Equal(t, 403, ce.Code()) assert.Equal(t, "不允许修改自己的部门和状态", ce.Error()) } // TC-0513: updateUser未登录被拒绝 func TestUpdateUser_NotLoggedInRejected(t *testing.T) { ctx := context.Background() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) logic := NewUpdateUserLogic(ctx, svcCtx) err := logic.UpdateUser(&types.UpdateUserReq{Id: 1, Nickname: strPtr("hacked")}) require.Error(t, err) var ce *response.CodeError require.True(t, errors.As(err, &ce)) assert.Equal(t, 401, ce.Code()) assert.Equal(t, "未登录", ce.Error()) }