84 lines
2.7 KiB
Go
84 lines
2.7 KiB
Go
//go:build unit
|
|||
|
|
|
||
|
|
package service
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"testing"
|
||
|
|
|
||
|
|
"github.com/stretchr/testify/require"
|
||
|
|
)
|
||
|
|
|
||
|
|
// 这些用例锁死"每个入口只声明自己真正要改的列":
|
||
|
|
// 任何退回整行回写的改动都会让并发写入被陈旧快照覆盖,并在这里变红。
|
||
|
|
|
||
|
|
func TestUpdateProfile_OnlyDeclaresRequestedColumns(t *testing.T) {
|
||
|
|
username := "renamed"
|
||
|
|
tests := []struct {
|
||
|
|
name string
|
||
|
|
req UpdateProfileRequest
|
||
|
|
want UserUpdateFields
|
||
|
|
}{
|
||
|
|
{
|
||
|
|
name: "username only",
|
||
|
|
req: UpdateProfileRequest{Username: &username},
|
||
|
|
want: UserUpdateFields{Username: true},
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: "notify settings only",
|
||
|
|
req: UpdateProfileRequest{BalanceNotifyEnabled: boolPtr(true)},
|
||
|
|
want: UserUpdateFields{BalanceNotifySettings: true},
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: "username and notify threshold",
|
||
|
|
req: UpdateProfileRequest{Username: &username, BalanceNotifyThreshold: float64Ptr(1.5)},
|
||
|
|
want: UserUpdateFields{Username: true, BalanceNotifySettings: true},
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
for _, tt := range tests {
|
||
|
|
t.Run(tt.name, func(t *testing.T) {
|
||
|
|
repo := &mockUserRepo{getByIDUser: &User{ID: 7, Balance: 0.30, Status: StatusActive}}
|
||
|
|
svc := NewUserService(repo, nil, nil, nil)
|
||
|
|
|
||
|
|
_, err := svc.UpdateProfile(context.Background(), 7, tt.req)
|
||
|
|
require.NoError(t, err)
|
||
|
|
require.Equal(t, []UserUpdateFields{tt.want}, repo.updateFields)
|
||
|
|
})
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// 只改头像时用户行没有任何列要写,不应产生一次整行更新。
|
||
|
|
func TestUpdateProfile_AvatarOnlySkipsUserRowWrite(t *testing.T) {
|
||
|
|
repo := &mockUserRepo{getByIDUser: &User{ID: 7, Balance: 0.30}}
|
||
|
|
svc := NewUserService(repo, nil, nil, nil)
|
||
|
|
|
||
|
|
avatar := "https://cdn.example.com/a.png"
|
||
|
|
_, err := svc.UpdateProfile(context.Background(), 7, UpdateProfileRequest{AvatarURL: &avatar})
|
||
|
|
require.NoError(t, err)
|
||
|
|
require.Len(t, repo.upsertAvatarArgs, 1, "avatar must still be stored")
|
||
|
|
require.Equal(t, []UserUpdateFields{{}}, repo.updateFields, "no user column should be declared")
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestChangePassword_OnlyDeclaresPasswordHash(t *testing.T) {
|
||
|
|
user := &User{ID: 7, Balance: 0.30}
|
||
|
|
require.NoError(t, user.SetPassword("old-password"))
|
||
|
|
repo := &mockUserRepo{getByIDUser: user}
|
||
|
|
svc := NewUserService(repo, nil, nil, nil)
|
||
|
|
|
||
|
|
err := svc.ChangePassword(context.Background(), 7, ChangePasswordRequest{
|
||
|
|
CurrentPassword: "old-password",
|
||
|
|
NewPassword: "new-password",
|
||
|
|
})
|
||
|
|
require.NoError(t, err)
|
||
|
|
require.Equal(t, []UserUpdateFields{{PasswordHash: true}}, repo.updateFields)
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestUpdateStatus_OnlyDeclaresStatus(t *testing.T) {
|
||
|
|
repo := &mockUserRepo{getByIDUser: &User{ID: 7, Balance: 0.30, Status: StatusActive}}
|
||
|
|
svc := NewUserService(repo, nil, nil, nil)
|
||
|
|
|
||
|
|
require.NoError(t, svc.UpdateStatus(context.Background(), 7, StatusDisabled))
|
||
|
|
require.Equal(t, []UserUpdateFields{{Status: true}}, repo.updateFields)
|
||
|
|
}
|