generated from VLADIMIR/template
fix
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"evening_detective_server/internal/modules/processor_jwt"
|
||||
"evening_detective_server/internal/repos"
|
||||
"evening_detective_server/internal/repos/users_repo"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
@@ -61,3 +62,34 @@ func TestChangeUserPassword(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Stop after successful password verification without using a real token database.
|
||||
type loginPasswordCheck struct {
|
||||
processor_jwt.IProcessorJWT
|
||||
err error
|
||||
}
|
||||
|
||||
func (p loginPasswordCheck) GenerateAccessToken(int, string, []string) (string, error) {
|
||||
return "", p.err
|
||||
}
|
||||
|
||||
func TestLoginAfterPasswordChangePreservesEmailCase(t *testing.T) {
|
||||
repo := newFakeRepo()
|
||||
repo.seed("admin@example.com", "unused")
|
||||
repo.byID[1].Roles = []string{"admin"}
|
||||
repo.seed("User@Example.COM", "old-hash")
|
||||
svc := &UsersService{usersRepo: repo, processorJWT: loginPasswordCheck{err: errors.New("stop before token storage")}}
|
||||
ctx := context.Background()
|
||||
if err := svc.ChangeUserPassword(ctx, 1, 2, "new password 123"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// This error can only occur after email lookup and bcrypt verification succeed.
|
||||
_, _, err := svc.Login(ctx, " User@Example.COM ", "new password 123")
|
||||
if err == nil || err.Error() != "Не получилось получить доступ" {
|
||||
t.Fatalf("lookup/password verification failed: %v", err)
|
||||
}
|
||||
_, _, err = svc.Login(ctx, "user@example.com", "new password 123")
|
||||
if !errors.Is(err, users_repo.ErrUserNotFound) {
|
||||
t.Fatalf("email case was changed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ func (s *UsersService) AddUser(
|
||||
return ErrTermsNotAccepted
|
||||
}
|
||||
|
||||
email = normalizeEmail(email)
|
||||
email = strings.TrimSpace(email)
|
||||
if err := validateEmail(email); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -182,7 +182,7 @@ func (s *UsersService) RefreshPassword(
|
||||
ctx context.Context,
|
||||
email string,
|
||||
) error {
|
||||
email = normalizeEmail(email)
|
||||
email = strings.TrimSpace(email)
|
||||
if err := validateEmail(email); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -222,12 +222,6 @@ func (s *UsersService) RefreshPassword(
|
||||
return s.usersRepo.UpdateUserPassword(ctx, user.Email, string(hashedPassword))
|
||||
}
|
||||
|
||||
// normalizeEmail приводит адрес к каноническому виду (без пробелов, нижний
|
||||
// регистр): "User@Example.COM " и "user@example.com" — один аккаунт.
|
||||
func normalizeEmail(email string) string {
|
||||
return strings.ToLower(strings.TrimSpace(email))
|
||||
}
|
||||
|
||||
// validateEmail проверяет, что строка является одиночным email-адресом.
|
||||
func validateEmail(email string) error {
|
||||
if email == "" {
|
||||
@@ -249,11 +243,10 @@ func (s *UsersService) Login(
|
||||
email string,
|
||||
password string,
|
||||
) (string, string, error) {
|
||||
// Email нормализуется так же, как при регистрации: иначе пользователь,
|
||||
// зарегистрировавшийся как user@example.com, не войдёт с User@Example.COM.
|
||||
// Сохраняем регистр email, как при регистрации; убираем только внешние пробелы.
|
||||
user, err := s.usersRepo.GetUserByEmail(
|
||||
ctx,
|
||||
normalizeEmail(email),
|
||||
strings.TrimSpace(email),
|
||||
)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
|
||||
@@ -175,17 +175,17 @@ func TestAddUserSuccess(t *testing.T) {
|
||||
t.Fatalf("AddUser: %v", err)
|
||||
}
|
||||
|
||||
// Email нормализован: пробелы убраны, регистр приведён.
|
||||
if _, ok := repo.users["user@example.com"]; !ok {
|
||||
t.Errorf("пользователь не создан с нормализованным email: %+v", repo.users)
|
||||
// Убираем внешние пробелы, сохраняя исходный регистр email.
|
||||
if _, ok := repo.users["User@Example.COM"]; !ok {
|
||||
t.Errorf("пользователь не создан с исходным регистром email: %+v", repo.users)
|
||||
}
|
||||
|
||||
msgs := sender.sent()
|
||||
if len(msgs) != 1 {
|
||||
t.Fatalf("ожидалось 1 письмо, отправлено %d", len(msgs))
|
||||
}
|
||||
if msgs[0].To != "user@example.com" {
|
||||
t.Errorf("письмо ушло на %q, want user@example.com", msgs[0].To)
|
||||
if msgs[0].To != "User@Example.COM" {
|
||||
t.Errorf("письмо ушло на %q, want User@Example.COM", msgs[0].To)
|
||||
}
|
||||
if !strings.Contains(msgs[0].Subject, "Вечернему детективу") {
|
||||
t.Errorf("неожиданная тема письма: %q", msgs[0].Subject)
|
||||
@@ -284,11 +284,11 @@ func TestAddUserRequiresTerms(t *testing.T) {
|
||||
|
||||
func TestRefreshPasswordSuccess(t *testing.T) {
|
||||
repo := newFakeRepo()
|
||||
repo.seed("user@example.com", "old-hash")
|
||||
repo.seed("User@Example.COM", "old-hash")
|
||||
sender := &fakeSender{}
|
||||
svc := newTestService(repo, sender)
|
||||
|
||||
// Ввод с пробелами и другим регистром — нормализуется.
|
||||
// Внешние пробелы убираются, регистр остаётся исходным.
|
||||
err := svc.RefreshPassword(context.Background(), " User@Example.COM ")
|
||||
if err != nil {
|
||||
t.Fatalf("RefreshPassword: %v", err)
|
||||
@@ -297,7 +297,7 @@ func TestRefreshPasswordSuccess(t *testing.T) {
|
||||
if len(sender.sent()) != 1 {
|
||||
t.Fatalf("ожидалось 1 письмо, отправлено %d", len(sender.sent()))
|
||||
}
|
||||
u, _ := repo.GetUserByEmail(context.Background(), "user@example.com")
|
||||
u, _ := repo.GetUserByEmail(context.Background(), "User@Example.COM")
|
||||
if u.PasswordHash == "old-hash" {
|
||||
t.Error("пароль не изменён")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user