package accounts import ( "context" "errors" "testing" "git.118im.com/im_team/im_workbench/internal/store" "golang.org/x/crypto/bcrypt" ) type profileRepository struct { overview *store.MemberOverview security *store.AccountSecurity loginPasswordHash string tradePasswordHash string } func (r *profileRepository) RegisterAccount(context.Context, store.AccountRegistration) (*store.Account, error) { return nil, errors.New("not implemented") } func (r *profileRepository) AccountByMobile(context.Context, int64, string) (*store.Account, error) { return nil, errors.New("not implemented") } func (r *profileRepository) RecordLogin(context.Context, int64, int64) error { return nil } func (r *profileRepository) MemberOverview(context.Context, int64, int64) (*store.MemberOverview, error) { return r.overview, nil } func (r *profileRepository) UpdateAvatar(context.Context, int64, int64, int64) (*store.Avatar, error) { return &store.Avatar{AttachmentID: 12, URL: "https://cdn.example/avatar.png"}, nil } func (r *profileRepository) AccountSecurity(context.Context, int64, int64) (*store.AccountSecurity, error) { return r.security, nil } func (r *profileRepository) UpdateLoginPassword(_ context.Context, _, _ int64, _, newHash string) error { r.loginPasswordHash = newHash return nil } func (r *profileRepository) UpdateTradePassword(_ context.Context, _, _ int64, _, newHash string) error { r.tradePasswordHash = newHash return nil } func TestProfileMapsMemberCategory(t *testing.T) { repository := &profileRepository{overview: &store.MemberOverview{ MemberID: 7, MemberNo: "M0007", Username: "robot", MemberStatus: 1, MemberCategory: 2, Mobile: "13800138000", Currency: "CNY", WalletStatus: 1, }} service := &Service{repository: repository, ready: true} profile, err := service.Profile(context.Background(), 0, 7) if err != nil { t.Fatal(err) } if profile.Phone != "13800138000" || profile.MemberCategory != "fake" || profile.MemberStatus != "active" || profile.Wallet.Status != "active" { t.Fatalf("unexpected profile states: %+v", profile) } } func TestChangePasswordVerifiesAndHashes(t *testing.T) { currentHash, err := bcrypt.GenerateFromPassword([]byte("old-secret"), bcrypt.MinCost) if err != nil { t.Fatal(err) } repository := &profileRepository{security: &store.AccountSecurity{PasswordHash: string(currentHash), MemberStatus: 1}} service := &Service{repository: repository, ready: true} if err := service.ChangePassword(context.Background(), 0, 7, ChangePasswordInput{ OldPassword: "old-secret", NewPassword: "new-secret", }); err != nil { t.Fatal(err) } if bcrypt.CompareHashAndPassword([]byte(repository.loginPasswordHash), []byte("new-secret")) != nil { t.Fatal("new login password was not bcrypt hashed") } } func TestChangePasswordRejectsWrongCurrentPassword(t *testing.T) { currentHash, _ := bcrypt.GenerateFromPassword([]byte("old-secret"), bcrypt.MinCost) repository := &profileRepository{security: &store.AccountSecurity{PasswordHash: string(currentHash)}} service := &Service{repository: repository, ready: true} err := service.ChangePassword(context.Background(), 0, 7, ChangePasswordInput{ OldPassword: "wrong-secret", NewPassword: "new-secret", }) if !errors.Is(err, ErrCurrentPassword) { t.Fatalf("error = %v, want %v", err, ErrCurrentPassword) } } func TestSetTradePasswordRequiresSixDigits(t *testing.T) { loginHash, _ := bcrypt.GenerateFromPassword([]byte("login-secret"), bcrypt.MinCost) repository := &profileRepository{security: &store.AccountSecurity{PasswordHash: string(loginHash)}} service := &Service{repository: repository, ready: true} for _, password := range []string{"", "12345", "1234567", "abcdef"} { err := service.ChangeTradePassword(context.Background(), 0, 7, ChangeTradePasswordInput{ LoginPassword: "login-secret", NewTradePassword: password, }) if !errors.Is(err, ErrTradePassword) { t.Errorf("password %q: error = %v, want %v", password, err, ErrTradePassword) } } if err := service.ChangeTradePassword(context.Background(), 0, 7, ChangeTradePasswordInput{ LoginPassword: "login-secret", NewTradePassword: "654321", }); err != nil { t.Fatal(err) } if bcrypt.CompareHashAndPassword([]byte(repository.tradePasswordHash), []byte("654321")) != nil { t.Fatal("trade password was not bcrypt hashed") } } func TestChangeTradePasswordRequiresOldPassword(t *testing.T) { loginHash, _ := bcrypt.GenerateFromPassword([]byte("login-secret"), bcrypt.MinCost) tradeHash, _ := bcrypt.GenerateFromPassword([]byte("123456"), bcrypt.MinCost) repository := &profileRepository{security: &store.AccountSecurity{ PasswordHash: string(loginHash), TradePasswordHash: string(tradeHash), }} service := &Service{repository: repository, ready: true} err := service.ChangeTradePassword(context.Background(), 0, 7, ChangeTradePasswordInput{ LoginPassword: "login-secret", NewTradePassword: "654321", }) if !errors.Is(err, ErrOldTradePasswordUnset) { t.Fatalf("error = %v, want %v", err, ErrOldTradePasswordUnset) } }