user_service_update_fields_test.go 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. //go:build unit
  2. package service
  3. import (
  4. "context"
  5. "testing"
  6. "github.com/stretchr/testify/require"
  7. )
  8. // 这些用例锁死"每个入口只声明自己真正要改的列":
  9. // 任何退回整行回写的改动都会让并发写入被陈旧快照覆盖,并在这里变红。
  10. func TestUpdateProfile_OnlyDeclaresRequestedColumns(t *testing.T) {
  11. username := "renamed"
  12. tests := []struct {
  13. name string
  14. req UpdateProfileRequest
  15. want UserUpdateFields
  16. }{
  17. {
  18. name: "username only",
  19. req: UpdateProfileRequest{Username: &username},
  20. want: UserUpdateFields{Username: true},
  21. },
  22. {
  23. name: "notify settings only",
  24. req: UpdateProfileRequest{BalanceNotifyEnabled: boolPtr(true)},
  25. want: UserUpdateFields{BalanceNotifySettings: true},
  26. },
  27. {
  28. name: "username and notify threshold",
  29. req: UpdateProfileRequest{Username: &username, BalanceNotifyThreshold: float64Ptr(1.5)},
  30. want: UserUpdateFields{Username: true, BalanceNotifySettings: true},
  31. },
  32. }
  33. for _, tt := range tests {
  34. t.Run(tt.name, func(t *testing.T) {
  35. repo := &mockUserRepo{getByIDUser: &User{ID: 7, Balance: 0.30, Status: StatusActive}}
  36. svc := NewUserService(repo, nil, nil, nil)
  37. _, err := svc.UpdateProfile(context.Background(), 7, tt.req)
  38. require.NoError(t, err)
  39. require.Equal(t, []UserUpdateFields{tt.want}, repo.updateFields)
  40. })
  41. }
  42. }
  43. // 只改头像时用户行没有任何列要写,不应产生一次整行更新。
  44. func TestUpdateProfile_AvatarOnlySkipsUserRowWrite(t *testing.T) {
  45. repo := &mockUserRepo{getByIDUser: &User{ID: 7, Balance: 0.30}}
  46. svc := NewUserService(repo, nil, nil, nil)
  47. avatar := "https://cdn.example.com/a.png"
  48. _, err := svc.UpdateProfile(context.Background(), 7, UpdateProfileRequest{AvatarURL: &avatar})
  49. require.NoError(t, err)
  50. require.Len(t, repo.upsertAvatarArgs, 1, "avatar must still be stored")
  51. require.Equal(t, []UserUpdateFields{{}}, repo.updateFields, "no user column should be declared")
  52. }
  53. func TestChangePassword_OnlyDeclaresPasswordHash(t *testing.T) {
  54. user := &User{ID: 7, Balance: 0.30}
  55. require.NoError(t, user.SetPassword("old-password"))
  56. repo := &mockUserRepo{getByIDUser: user}
  57. svc := NewUserService(repo, nil, nil, nil)
  58. err := svc.ChangePassword(context.Background(), 7, ChangePasswordRequest{
  59. CurrentPassword: "old-password",
  60. NewPassword: "new-password",
  61. })
  62. require.NoError(t, err)
  63. require.Equal(t, []UserUpdateFields{{PasswordHash: true}}, repo.updateFields)
  64. }
  65. func TestUpdateStatus_OnlyDeclaresStatus(t *testing.T) {
  66. repo := &mockUserRepo{getByIDUser: &User{ID: 7, Balance: 0.30, Status: StatusActive}}
  67. svc := NewUserService(repo, nil, nil, nil)
  68. require.NoError(t, svc.UpdateStatus(context.Background(), 7, StatusDisabled))
  69. require.Equal(t, []UserUpdateFields{{Status: true}}, repo.updateFields)
  70. }