To gitea and beyond, let's go(-yco)
This commit is contained in:
579
internal/services/registration_service_test.go
Normal file
579
internal/services/registration_service_test.go
Normal file
@@ -0,0 +1,579 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"goyco/internal/config"
|
||||
"goyco/internal/database"
|
||||
"goyco/internal/testutils"
|
||||
)
|
||||
|
||||
func TestNewRegistrationService(t *testing.T) {
|
||||
userRepo := testutils.NewMockUserRepository()
|
||||
emailService, _ := NewEmailService(testutils.AppTestConfig, &testutils.MockEmailSender{})
|
||||
cfg := testutils.AppTestConfig
|
||||
|
||||
service := NewRegistrationService(userRepo, emailService, cfg)
|
||||
|
||||
if service == nil {
|
||||
t.Fatal("expected service to be created")
|
||||
}
|
||||
|
||||
if service.userRepo != userRepo {
|
||||
t.Error("expected userRepo to be set")
|
||||
}
|
||||
|
||||
if service.emailService != emailService {
|
||||
t.Error("expected emailService to be set")
|
||||
}
|
||||
|
||||
if service.config != cfg {
|
||||
t.Error("expected config to be set")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegistrationService_Register(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
username string
|
||||
email string
|
||||
password string
|
||||
setupMocks func() (*testutils.MockUserRepository, *EmailService, *config.Config)
|
||||
expectedError error
|
||||
checkResult func(*testing.T, *RegistrationResult)
|
||||
}{
|
||||
{
|
||||
name: "successful registration",
|
||||
username: "testuser",
|
||||
email: "test@example.com",
|
||||
password: "SecurePass123!",
|
||||
setupMocks: func() (*testutils.MockUserRepository, *EmailService, *config.Config) {
|
||||
userRepo := testutils.NewMockUserRepository()
|
||||
emailSender := &testutils.MockEmailSender{}
|
||||
emailService, _ := NewEmailService(testutils.AppTestConfig, emailSender)
|
||||
return userRepo, emailService, testutils.AppTestConfig
|
||||
},
|
||||
expectedError: nil,
|
||||
checkResult: func(t *testing.T, result *RegistrationResult) {
|
||||
if result == nil {
|
||||
t.Fatal("expected non-nil result")
|
||||
}
|
||||
if result.User == nil {
|
||||
t.Fatal("expected non-nil user")
|
||||
}
|
||||
if result.User.Username != "testuser" {
|
||||
t.Errorf("expected username 'testuser', got %q", result.User.Username)
|
||||
}
|
||||
if result.User.Email != "test@example.com" {
|
||||
t.Errorf("expected email 'test@example.com', got %q", result.User.Email)
|
||||
}
|
||||
if result.User.Password != "" {
|
||||
t.Error("expected password to be sanitized")
|
||||
}
|
||||
if !result.VerificationSent {
|
||||
t.Error("expected VerificationSent to be true")
|
||||
}
|
||||
if result.User.EmailVerified {
|
||||
t.Error("expected EmailVerified to be false")
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid username",
|
||||
username: "",
|
||||
email: "test@example.com",
|
||||
password: "SecurePass123!",
|
||||
setupMocks: func() (*testutils.MockUserRepository, *EmailService, *config.Config) {
|
||||
userRepo := testutils.NewMockUserRepository()
|
||||
emailService, _ := NewEmailService(testutils.AppTestConfig, &testutils.MockEmailSender{})
|
||||
return userRepo, emailService, testutils.AppTestConfig
|
||||
},
|
||||
expectedError: nil,
|
||||
checkResult: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid password",
|
||||
username: "testuser",
|
||||
email: "test@example.com",
|
||||
password: "short",
|
||||
setupMocks: func() (*testutils.MockUserRepository, *EmailService, *config.Config) {
|
||||
userRepo := testutils.NewMockUserRepository()
|
||||
emailService, _ := NewEmailService(testutils.AppTestConfig, &testutils.MockEmailSender{})
|
||||
return userRepo, emailService, testutils.AppTestConfig
|
||||
},
|
||||
expectedError: nil,
|
||||
checkResult: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid email",
|
||||
username: "testuser",
|
||||
email: "invalid-email",
|
||||
password: "SecurePass123!",
|
||||
setupMocks: func() (*testutils.MockUserRepository, *EmailService, *config.Config) {
|
||||
userRepo := testutils.NewMockUserRepository()
|
||||
emailService, _ := NewEmailService(testutils.AppTestConfig, &testutils.MockEmailSender{})
|
||||
return userRepo, emailService, testutils.AppTestConfig
|
||||
},
|
||||
expectedError: nil,
|
||||
checkResult: nil,
|
||||
},
|
||||
{
|
||||
name: "username already taken",
|
||||
username: "existinguser",
|
||||
email: "test@example.com",
|
||||
password: "SecurePass123!",
|
||||
setupMocks: func() (*testutils.MockUserRepository, *EmailService, *config.Config) {
|
||||
userRepo := testutils.NewMockUserRepository()
|
||||
existingUser := &database.User{
|
||||
ID: 1,
|
||||
Username: "existinguser",
|
||||
Email: "existing@example.com",
|
||||
}
|
||||
userRepo.Create(existingUser)
|
||||
emailService, _ := NewEmailService(testutils.AppTestConfig, &testutils.MockEmailSender{})
|
||||
return userRepo, emailService, testutils.AppTestConfig
|
||||
},
|
||||
expectedError: ErrUsernameTaken,
|
||||
checkResult: nil,
|
||||
},
|
||||
{
|
||||
name: "email already taken",
|
||||
username: "testuser",
|
||||
email: "existing@example.com",
|
||||
password: "SecurePass123!",
|
||||
setupMocks: func() (*testutils.MockUserRepository, *EmailService, *config.Config) {
|
||||
userRepo := testutils.NewMockUserRepository()
|
||||
existingUser := &database.User{
|
||||
ID: 1,
|
||||
Username: "existinguser",
|
||||
Email: "existing@example.com",
|
||||
}
|
||||
userRepo.Create(existingUser)
|
||||
emailService, _ := NewEmailService(testutils.AppTestConfig, &testutils.MockEmailSender{})
|
||||
return userRepo, emailService, testutils.AppTestConfig
|
||||
},
|
||||
expectedError: ErrEmailTaken,
|
||||
checkResult: nil,
|
||||
},
|
||||
{
|
||||
name: "email service error",
|
||||
username: "testuser",
|
||||
email: "test@example.com",
|
||||
password: "SecurePass123!",
|
||||
setupMocks: func() (*testutils.MockUserRepository, *EmailService, *config.Config) {
|
||||
userRepo := testutils.NewMockUserRepository()
|
||||
errorSender := &errorEmailSender{err: errors.New("email service error")}
|
||||
emailService, _ := NewEmailService(testutils.AppTestConfig, errorSender)
|
||||
return userRepo, emailService, testutils.AppTestConfig
|
||||
},
|
||||
expectedError: nil,
|
||||
checkResult: nil,
|
||||
},
|
||||
{
|
||||
name: "trims username whitespace",
|
||||
username: " testuser ",
|
||||
email: "test@example.com",
|
||||
password: "SecurePass123!",
|
||||
setupMocks: func() (*testutils.MockUserRepository, *EmailService, *config.Config) {
|
||||
userRepo := testutils.NewMockUserRepository()
|
||||
emailSender := &testutils.MockEmailSender{}
|
||||
emailService, _ := NewEmailService(testutils.AppTestConfig, emailSender)
|
||||
return userRepo, emailService, testutils.AppTestConfig
|
||||
},
|
||||
expectedError: nil,
|
||||
checkResult: func(t *testing.T, result *RegistrationResult) {
|
||||
if result.User.Username != "testuser" {
|
||||
t.Errorf("expected trimmed username 'testuser', got %q", result.User.Username)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "normalizes email",
|
||||
username: "testuser",
|
||||
email: "TEST@EXAMPLE.COM",
|
||||
password: "SecurePass123!",
|
||||
setupMocks: func() (*testutils.MockUserRepository, *EmailService, *config.Config) {
|
||||
userRepo := testutils.NewMockUserRepository()
|
||||
emailSender := &testutils.MockEmailSender{}
|
||||
emailService, _ := NewEmailService(testutils.AppTestConfig, emailSender)
|
||||
return userRepo, emailService, testutils.AppTestConfig
|
||||
},
|
||||
expectedError: nil,
|
||||
checkResult: func(t *testing.T, result *RegistrationResult) {
|
||||
if result.User.Email != "test@example.com" {
|
||||
t.Errorf("expected normalized email 'test@example.com', got %q", result.User.Email)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
userRepo, emailService, cfg := tt.setupMocks()
|
||||
service := NewRegistrationService(userRepo, emailService, cfg)
|
||||
|
||||
result, err := service.Register(tt.username, tt.email, tt.password)
|
||||
|
||||
if tt.expectedError != nil {
|
||||
if err == nil {
|
||||
t.Fatal("expected error, got nil")
|
||||
}
|
||||
if !errors.Is(err, tt.expectedError) {
|
||||
t.Errorf("expected error %v, got %v", tt.expectedError, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if tt.checkResult == nil {
|
||||
return
|
||||
}
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if tt.checkResult != nil {
|
||||
tt.checkResult(t, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegistrationService_ConfirmEmail(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
token string
|
||||
setupMocks func() (*testutils.MockUserRepository, *EmailService, *config.Config)
|
||||
expectedError error
|
||||
checkResult func(*testing.T, *database.User)
|
||||
}{
|
||||
{
|
||||
name: "successful confirmation",
|
||||
token: "valid-token",
|
||||
setupMocks: func() (*testutils.MockUserRepository, *EmailService, *config.Config) {
|
||||
userRepo := testutils.NewMockUserRepository()
|
||||
hashedToken := HashVerificationToken("valid-token")
|
||||
user := &database.User{
|
||||
ID: 1,
|
||||
Username: "testuser",
|
||||
Email: "test@example.com",
|
||||
EmailVerified: false,
|
||||
EmailVerificationToken: hashedToken,
|
||||
}
|
||||
userRepo.Create(user)
|
||||
emailService, _ := NewEmailService(testutils.AppTestConfig, &testutils.MockEmailSender{})
|
||||
return userRepo, emailService, testutils.AppTestConfig
|
||||
},
|
||||
expectedError: nil,
|
||||
checkResult: func(t *testing.T, user *database.User) {
|
||||
if user == nil {
|
||||
t.Fatal("expected non-nil user")
|
||||
}
|
||||
if !user.EmailVerified {
|
||||
t.Error("expected EmailVerified to be true")
|
||||
}
|
||||
if user.EmailVerificationToken != "" {
|
||||
t.Error("expected EmailVerificationToken to be cleared")
|
||||
}
|
||||
if user.EmailVerificationSentAt != nil {
|
||||
t.Error("expected EmailVerificationSentAt to be nil")
|
||||
}
|
||||
if user.EmailVerifiedAt == nil {
|
||||
t.Error("expected EmailVerifiedAt to be set")
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty token",
|
||||
token: "",
|
||||
setupMocks: func() (*testutils.MockUserRepository, *EmailService, *config.Config) {
|
||||
userRepo := testutils.NewMockUserRepository()
|
||||
emailService, _ := NewEmailService(testutils.AppTestConfig, &testutils.MockEmailSender{})
|
||||
return userRepo, emailService, testutils.AppTestConfig
|
||||
},
|
||||
expectedError: ErrInvalidVerificationToken,
|
||||
checkResult: nil,
|
||||
},
|
||||
{
|
||||
name: "whitespace only token",
|
||||
token: " ",
|
||||
setupMocks: func() (*testutils.MockUserRepository, *EmailService, *config.Config) {
|
||||
userRepo := testutils.NewMockUserRepository()
|
||||
emailService, _ := NewEmailService(testutils.AppTestConfig, &testutils.MockEmailSender{})
|
||||
return userRepo, emailService, testutils.AppTestConfig
|
||||
},
|
||||
expectedError: ErrInvalidVerificationToken,
|
||||
checkResult: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid token",
|
||||
token: "invalid-token",
|
||||
setupMocks: func() (*testutils.MockUserRepository, *EmailService, *config.Config) {
|
||||
userRepo := testutils.NewMockUserRepository()
|
||||
emailService, _ := NewEmailService(testutils.AppTestConfig, &testutils.MockEmailSender{})
|
||||
return userRepo, emailService, testutils.AppTestConfig
|
||||
},
|
||||
expectedError: ErrInvalidVerificationToken,
|
||||
checkResult: nil,
|
||||
},
|
||||
{
|
||||
name: "already verified",
|
||||
token: "valid-token",
|
||||
setupMocks: func() (*testutils.MockUserRepository, *EmailService, *config.Config) {
|
||||
userRepo := testutils.NewMockUserRepository()
|
||||
hashedToken := HashVerificationToken("valid-token")
|
||||
now := time.Now()
|
||||
user := &database.User{
|
||||
ID: 1,
|
||||
Username: "testuser",
|
||||
Email: "test@example.com",
|
||||
EmailVerified: true,
|
||||
EmailVerifiedAt: &now,
|
||||
EmailVerificationToken: hashedToken,
|
||||
}
|
||||
userRepo.Create(user)
|
||||
emailService, _ := NewEmailService(testutils.AppTestConfig, &testutils.MockEmailSender{})
|
||||
return userRepo, emailService, testutils.AppTestConfig
|
||||
},
|
||||
expectedError: nil,
|
||||
checkResult: func(t *testing.T, user *database.User) {
|
||||
if user == nil {
|
||||
t.Fatal("expected non-nil user")
|
||||
}
|
||||
if !user.EmailVerified {
|
||||
t.Error("expected EmailVerified to be true")
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "trims token whitespace",
|
||||
token: " valid-token ",
|
||||
setupMocks: func() (*testutils.MockUserRepository, *EmailService, *config.Config) {
|
||||
userRepo := testutils.NewMockUserRepository()
|
||||
hashedToken := HashVerificationToken("valid-token")
|
||||
user := &database.User{
|
||||
ID: 1,
|
||||
Username: "testuser",
|
||||
Email: "test@example.com",
|
||||
EmailVerified: false,
|
||||
EmailVerificationToken: hashedToken,
|
||||
}
|
||||
userRepo.Create(user)
|
||||
emailService, _ := NewEmailService(testutils.AppTestConfig, &testutils.MockEmailSender{})
|
||||
return userRepo, emailService, testutils.AppTestConfig
|
||||
},
|
||||
expectedError: nil,
|
||||
checkResult: func(t *testing.T, user *database.User) {
|
||||
if !user.EmailVerified {
|
||||
t.Error("expected EmailVerified to be true")
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
userRepo, emailService, cfg := tt.setupMocks()
|
||||
service := NewRegistrationService(userRepo, emailService, cfg)
|
||||
|
||||
user, err := service.ConfirmEmail(tt.token)
|
||||
|
||||
if tt.expectedError != nil {
|
||||
if err == nil {
|
||||
t.Fatal("expected error, got nil")
|
||||
}
|
||||
if !errors.Is(err, tt.expectedError) {
|
||||
t.Errorf("expected error %v, got %v", tt.expectedError, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if tt.checkResult == nil {
|
||||
return
|
||||
}
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if tt.checkResult != nil {
|
||||
tt.checkResult(t, user)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegistrationService_ResendVerificationEmail(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
email string
|
||||
setupMocks func() (*testutils.MockUserRepository, *EmailService, *config.Config)
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
name: "successful resend",
|
||||
email: "test@example.com",
|
||||
setupMocks: func() (*testutils.MockUserRepository, *EmailService, *config.Config) {
|
||||
userRepo := testutils.NewMockUserRepository()
|
||||
oldTime := time.Now().Add(-10 * time.Minute)
|
||||
user := &database.User{
|
||||
ID: 1,
|
||||
Username: "testuser",
|
||||
Email: "test@example.com",
|
||||
EmailVerified: false,
|
||||
EmailVerificationSentAt: &oldTime,
|
||||
}
|
||||
userRepo.Create(user)
|
||||
emailSender := &testutils.MockEmailSender{}
|
||||
emailService, _ := NewEmailService(testutils.AppTestConfig, emailSender)
|
||||
return userRepo, emailService, testutils.AppTestConfig
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid email",
|
||||
email: "invalid-email",
|
||||
setupMocks: func() (*testutils.MockUserRepository, *EmailService, *config.Config) {
|
||||
userRepo := testutils.NewMockUserRepository()
|
||||
emailService, _ := NewEmailService(testutils.AppTestConfig, &testutils.MockEmailSender{})
|
||||
return userRepo, emailService, testutils.AppTestConfig
|
||||
},
|
||||
expectedError: ErrInvalidEmail,
|
||||
},
|
||||
{
|
||||
name: "user not found",
|
||||
email: "nonexistent@example.com",
|
||||
setupMocks: func() (*testutils.MockUserRepository, *EmailService, *config.Config) {
|
||||
userRepo := testutils.NewMockUserRepository()
|
||||
emailService, _ := NewEmailService(testutils.AppTestConfig, &testutils.MockEmailSender{})
|
||||
return userRepo, emailService, testutils.AppTestConfig
|
||||
},
|
||||
expectedError: ErrInvalidCredentials,
|
||||
},
|
||||
{
|
||||
name: "email already verified",
|
||||
email: "test@example.com",
|
||||
setupMocks: func() (*testutils.MockUserRepository, *EmailService, *config.Config) {
|
||||
userRepo := testutils.NewMockUserRepository()
|
||||
now := time.Now()
|
||||
user := &database.User{
|
||||
ID: 1,
|
||||
Username: "testuser",
|
||||
Email: "test@example.com",
|
||||
EmailVerified: true,
|
||||
EmailVerifiedAt: &now,
|
||||
}
|
||||
userRepo.Create(user)
|
||||
emailService, _ := NewEmailService(testutils.AppTestConfig, &testutils.MockEmailSender{})
|
||||
return userRepo, emailService, testutils.AppTestConfig
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "email sent too recently",
|
||||
email: "test@example.com",
|
||||
setupMocks: func() (*testutils.MockUserRepository, *EmailService, *config.Config) {
|
||||
userRepo := testutils.NewMockUserRepository()
|
||||
recentTime := time.Now().Add(-2 * time.Minute)
|
||||
user := &database.User{
|
||||
ID: 1,
|
||||
Username: "testuser",
|
||||
Email: "test@example.com",
|
||||
EmailVerified: false,
|
||||
EmailVerificationSentAt: &recentTime,
|
||||
}
|
||||
userRepo.Create(user)
|
||||
emailService, _ := NewEmailService(testutils.AppTestConfig, &testutils.MockEmailSender{})
|
||||
return userRepo, emailService, testutils.AppTestConfig
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "email service error",
|
||||
email: "test@example.com",
|
||||
setupMocks: func() (*testutils.MockUserRepository, *EmailService, *config.Config) {
|
||||
userRepo := testutils.NewMockUserRepository()
|
||||
oldTime := time.Now().Add(-10 * time.Minute)
|
||||
user := &database.User{
|
||||
ID: 1,
|
||||
Username: "testuser",
|
||||
Email: "test@example.com",
|
||||
EmailVerified: false,
|
||||
EmailVerificationSentAt: &oldTime,
|
||||
}
|
||||
userRepo.Create(user)
|
||||
errorSender := &errorEmailSender{err: errors.New("email service error")}
|
||||
emailService, _ := NewEmailService(testutils.AppTestConfig, errorSender)
|
||||
return userRepo, emailService, testutils.AppTestConfig
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "trims email whitespace",
|
||||
email: " test@example.com ",
|
||||
setupMocks: func() (*testutils.MockUserRepository, *EmailService, *config.Config) {
|
||||
userRepo := testutils.NewMockUserRepository()
|
||||
oldTime := time.Now().Add(-10 * time.Minute)
|
||||
user := &database.User{
|
||||
ID: 1,
|
||||
Username: "testuser",
|
||||
Email: "test@example.com",
|
||||
EmailVerified: false,
|
||||
EmailVerificationSentAt: &oldTime,
|
||||
}
|
||||
userRepo.Create(user)
|
||||
emailSender := &testutils.MockEmailSender{}
|
||||
emailService, _ := NewEmailService(testutils.AppTestConfig, emailSender)
|
||||
return userRepo, emailService, testutils.AppTestConfig
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "no previous verification sent",
|
||||
email: "test@example.com",
|
||||
setupMocks: func() (*testutils.MockUserRepository, *EmailService, *config.Config) {
|
||||
userRepo := testutils.NewMockUserRepository()
|
||||
user := &database.User{
|
||||
ID: 1,
|
||||
Username: "testuser",
|
||||
Email: "test@example.com",
|
||||
EmailVerified: false,
|
||||
}
|
||||
userRepo.Create(user)
|
||||
emailSender := &testutils.MockEmailSender{}
|
||||
emailService, _ := NewEmailService(testutils.AppTestConfig, emailSender)
|
||||
return userRepo, emailService, testutils.AppTestConfig
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
userRepo, emailService, cfg := tt.setupMocks()
|
||||
service := NewRegistrationService(userRepo, emailService, cfg)
|
||||
|
||||
err := service.ResendVerificationEmail(tt.email)
|
||||
|
||||
if tt.expectedError != nil {
|
||||
if err == nil {
|
||||
t.Fatal("expected error, got nil")
|
||||
}
|
||||
if !errors.Is(err, tt.expectedError) {
|
||||
t.Errorf("expected error %v, got %v", tt.expectedError, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if tt.name == "email already verified" || tt.name == "email sent too recently" || tt.name == "email service error" {
|
||||
if err.Error() == "" {
|
||||
t.Fatal("expected error message")
|
||||
}
|
||||
return
|
||||
}
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user