Coverage priorities 1-5: test pure functions, extract interfaces, mock-based handler tests
- Priority 1: Test NewSendEmailTask + NewSendPushTask (5 tests) - Priority 2: Test customHTTPErrorHandler — all 15+ branches (21 tests) - Priority 3: Extract Enqueuer interface + payload builders in worker pkg (5 tests) - Priority 4: Extract ClassifyFile/ComputeRelPath in migrate-encrypt (6 tests) - Priority 5: Define Handler interfaces, refactor to accept them, mock-based tests (14 tests) - Fix .gitignore: /worker instead of worker to stop ignoring internal/worker/ Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
800
internal/services/auth_service_test.go
Normal file
800
internal/services/auth_service_test.go
Normal file
@@ -0,0 +1,800 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/treytartt/honeydue-api/internal/config"
|
||||
"github.com/treytartt/honeydue-api/internal/dto/requests"
|
||||
"github.com/treytartt/honeydue-api/internal/repositories"
|
||||
"github.com/treytartt/honeydue-api/internal/testutil"
|
||||
)
|
||||
|
||||
func setupAuthService(t *testing.T) (*AuthService, *repositories.UserRepository) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
userRepo := repositories.NewUserRepository(db)
|
||||
notifRepo := repositories.NewNotificationRepository(db)
|
||||
cfg := &config.Config{
|
||||
Server: config.ServerConfig{
|
||||
DebugFixedCodes: true,
|
||||
},
|
||||
Security: config.SecurityConfig{
|
||||
SecretKey: "test-secret",
|
||||
ConfirmationExpiry: 24 * time.Hour,
|
||||
PasswordResetExpiry: 15 * time.Minute,
|
||||
MaxPasswordResetRate: 3,
|
||||
TokenExpiryDays: 90,
|
||||
TokenRefreshDays: 60,
|
||||
},
|
||||
}
|
||||
service := NewAuthService(userRepo, cfg)
|
||||
service.SetNotificationRepository(notifRepo)
|
||||
return service, userRepo
|
||||
}
|
||||
|
||||
// === Login ===
|
||||
|
||||
func TestAuthService_Login(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
userRepo := repositories.NewUserRepository(db)
|
||||
cfg := &config.Config{
|
||||
Security: config.SecurityConfig{SecretKey: "test-secret"},
|
||||
}
|
||||
service := NewAuthService(userRepo, cfg)
|
||||
|
||||
testutil.CreateTestUser(t, db, "testuser", "test@test.com", "Password123")
|
||||
|
||||
req := &requests.LoginRequest{
|
||||
Username: "testuser",
|
||||
Password: "Password123",
|
||||
}
|
||||
|
||||
resp, err := service.Login(req)
|
||||
require.NoError(t, err)
|
||||
assert.NotEmpty(t, resp.Token)
|
||||
assert.Equal(t, "testuser", resp.User.Username)
|
||||
}
|
||||
|
||||
func TestAuthService_Login_ByEmail(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
userRepo := repositories.NewUserRepository(db)
|
||||
cfg := &config.Config{
|
||||
Security: config.SecurityConfig{SecretKey: "test-secret"},
|
||||
}
|
||||
service := NewAuthService(userRepo, cfg)
|
||||
|
||||
testutil.CreateTestUser(t, db, "testuser", "test@test.com", "Password123")
|
||||
|
||||
req := &requests.LoginRequest{
|
||||
Email: "test@test.com",
|
||||
Password: "Password123",
|
||||
}
|
||||
|
||||
resp, err := service.Login(req)
|
||||
require.NoError(t, err)
|
||||
assert.NotEmpty(t, resp.Token)
|
||||
}
|
||||
|
||||
func TestAuthService_Login_InvalidCredentials(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
userRepo := repositories.NewUserRepository(db)
|
||||
cfg := &config.Config{
|
||||
Security: config.SecurityConfig{SecretKey: "test-secret"},
|
||||
}
|
||||
service := NewAuthService(userRepo, cfg)
|
||||
|
||||
testutil.CreateTestUser(t, db, "testuser", "test@test.com", "Password123")
|
||||
|
||||
req := &requests.LoginRequest{
|
||||
Username: "testuser",
|
||||
Password: "WrongPassword1",
|
||||
}
|
||||
|
||||
_, err := service.Login(req)
|
||||
testutil.AssertAppError(t, err, http.StatusUnauthorized, "error.invalid_credentials")
|
||||
}
|
||||
|
||||
func TestAuthService_Login_UserNotFound(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
userRepo := repositories.NewUserRepository(db)
|
||||
cfg := &config.Config{
|
||||
Security: config.SecurityConfig{SecretKey: "test-secret"},
|
||||
}
|
||||
service := NewAuthService(userRepo, cfg)
|
||||
|
||||
req := &requests.LoginRequest{
|
||||
Username: "nonexistent",
|
||||
Password: "Password123",
|
||||
}
|
||||
|
||||
_, err := service.Login(req)
|
||||
testutil.AssertAppError(t, err, http.StatusUnauthorized, "error.invalid_credentials")
|
||||
}
|
||||
|
||||
func TestAuthService_Login_InactiveUser(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
userRepo := repositories.NewUserRepository(db)
|
||||
cfg := &config.Config{
|
||||
Security: config.SecurityConfig{SecretKey: "test-secret"},
|
||||
}
|
||||
service := NewAuthService(userRepo, cfg)
|
||||
|
||||
user := testutil.CreateTestUser(t, db, "inactive", "inactive@test.com", "Password123")
|
||||
// Deactivate
|
||||
user.IsActive = false
|
||||
db.Save(user)
|
||||
|
||||
req := &requests.LoginRequest{
|
||||
Username: "inactive",
|
||||
Password: "Password123",
|
||||
}
|
||||
|
||||
_, err := service.Login(req)
|
||||
testutil.AssertAppError(t, err, http.StatusUnauthorized, "error.account_inactive")
|
||||
}
|
||||
|
||||
// === Register ===
|
||||
|
||||
func TestAuthService_Register(t *testing.T) {
|
||||
service, _ := setupAuthService(t)
|
||||
|
||||
req := &requests.RegisterRequest{
|
||||
Username: "newuser",
|
||||
Email: "new@test.com",
|
||||
Password: "Password123",
|
||||
}
|
||||
|
||||
resp, code, err := service.Register(req)
|
||||
require.NoError(t, err)
|
||||
assert.NotEmpty(t, resp.Token)
|
||||
assert.Equal(t, "newuser", resp.User.Username)
|
||||
assert.Equal(t, "123456", code) // DebugFixedCodes=true
|
||||
}
|
||||
|
||||
func TestAuthService_Register_DuplicateUsername(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
userRepo := repositories.NewUserRepository(db)
|
||||
cfg := &config.Config{
|
||||
Server: config.ServerConfig{DebugFixedCodes: true},
|
||||
Security: config.SecurityConfig{SecretKey: "test", ConfirmationExpiry: 24 * time.Hour},
|
||||
}
|
||||
service := NewAuthService(userRepo, cfg)
|
||||
|
||||
testutil.CreateTestUser(t, db, "taken", "taken@test.com", "Password123")
|
||||
|
||||
req := &requests.RegisterRequest{
|
||||
Username: "taken",
|
||||
Email: "different@test.com",
|
||||
Password: "Password123",
|
||||
}
|
||||
|
||||
_, _, err := service.Register(req)
|
||||
testutil.AssertAppError(t, err, http.StatusConflict, "error.username_taken")
|
||||
}
|
||||
|
||||
func TestAuthService_Register_DuplicateEmail(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
userRepo := repositories.NewUserRepository(db)
|
||||
cfg := &config.Config{
|
||||
Server: config.ServerConfig{DebugFixedCodes: true},
|
||||
Security: config.SecurityConfig{SecretKey: "test", ConfirmationExpiry: 24 * time.Hour},
|
||||
}
|
||||
service := NewAuthService(userRepo, cfg)
|
||||
|
||||
testutil.CreateTestUser(t, db, "existing", "taken@test.com", "Password123")
|
||||
|
||||
req := &requests.RegisterRequest{
|
||||
Username: "newuser",
|
||||
Email: "taken@test.com",
|
||||
Password: "Password123",
|
||||
}
|
||||
|
||||
_, _, err := service.Register(req)
|
||||
testutil.AssertAppError(t, err, http.StatusConflict, "error.email_taken")
|
||||
}
|
||||
|
||||
// === GetCurrentUser ===
|
||||
|
||||
func TestAuthService_GetCurrentUser(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
userRepo := repositories.NewUserRepository(db)
|
||||
cfg := &config.Config{
|
||||
Security: config.SecurityConfig{SecretKey: "test-secret"},
|
||||
}
|
||||
service := NewAuthService(userRepo, cfg)
|
||||
|
||||
user := testutil.CreateTestUser(t, db, "testuser", "test@test.com", "Password123")
|
||||
// Create profile
|
||||
userRepo.GetOrCreateProfile(user.ID)
|
||||
|
||||
resp, err := service.GetCurrentUser(user.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "testuser", resp.Username)
|
||||
assert.Equal(t, "test@test.com", resp.Email)
|
||||
assert.Equal(t, "email", resp.AuthProvider) // Default for no social auth
|
||||
}
|
||||
|
||||
// === UpdateProfile ===
|
||||
|
||||
func TestAuthService_UpdateProfile(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
userRepo := repositories.NewUserRepository(db)
|
||||
cfg := &config.Config{
|
||||
Security: config.SecurityConfig{SecretKey: "test-secret"},
|
||||
}
|
||||
service := NewAuthService(userRepo, cfg)
|
||||
|
||||
user := testutil.CreateTestUser(t, db, "testuser", "test@test.com", "Password123")
|
||||
userRepo.GetOrCreateProfile(user.ID)
|
||||
|
||||
newFirst := "John"
|
||||
newLast := "Doe"
|
||||
req := &requests.UpdateProfileRequest{
|
||||
FirstName: &newFirst,
|
||||
LastName: &newLast,
|
||||
}
|
||||
|
||||
resp, err := service.UpdateProfile(user.ID, req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "John", resp.FirstName)
|
||||
assert.Equal(t, "Doe", resp.LastName)
|
||||
}
|
||||
|
||||
func TestAuthService_UpdateProfile_DuplicateEmail(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
userRepo := repositories.NewUserRepository(db)
|
||||
cfg := &config.Config{
|
||||
Security: config.SecurityConfig{SecretKey: "test-secret"},
|
||||
}
|
||||
service := NewAuthService(userRepo, cfg)
|
||||
|
||||
testutil.CreateTestUser(t, db, "user1", "user1@test.com", "Password123")
|
||||
user2 := testutil.CreateTestUser(t, db, "user2", "user2@test.com", "Password123")
|
||||
userRepo.GetOrCreateProfile(user2.ID)
|
||||
|
||||
takenEmail := "user1@test.com"
|
||||
req := &requests.UpdateProfileRequest{
|
||||
Email: &takenEmail,
|
||||
}
|
||||
|
||||
_, err := service.UpdateProfile(user2.ID, req)
|
||||
testutil.AssertAppError(t, err, http.StatusConflict, "error.email_already_taken")
|
||||
}
|
||||
|
||||
func TestAuthService_UpdateProfile_SameEmail(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
userRepo := repositories.NewUserRepository(db)
|
||||
cfg := &config.Config{
|
||||
Security: config.SecurityConfig{SecretKey: "test-secret"},
|
||||
}
|
||||
service := NewAuthService(userRepo, cfg)
|
||||
|
||||
user := testutil.CreateTestUser(t, db, "testuser", "test@test.com", "Password123")
|
||||
userRepo.GetOrCreateProfile(user.ID)
|
||||
|
||||
sameEmail := "test@test.com"
|
||||
req := &requests.UpdateProfileRequest{
|
||||
Email: &sameEmail,
|
||||
}
|
||||
|
||||
// Same email should not trigger duplicate error
|
||||
resp, err := service.UpdateProfile(user.ID, req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "test@test.com", resp.Email)
|
||||
}
|
||||
|
||||
// === VerifyEmail ===
|
||||
|
||||
func TestAuthService_VerifyEmail(t *testing.T) {
|
||||
service, _ := setupAuthService(t)
|
||||
|
||||
// Register a user (creates confirmation code)
|
||||
req := &requests.RegisterRequest{
|
||||
Username: "newuser",
|
||||
Email: "new@test.com",
|
||||
Password: "Password123",
|
||||
}
|
||||
_, _, err := service.Register(req)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Get the user ID
|
||||
user, err := service.userRepo.FindByEmail("new@test.com")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify with the debug code
|
||||
err = service.VerifyEmail(user.ID, "123456")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify again — should get already verified error
|
||||
err = service.VerifyEmail(user.ID, "123456")
|
||||
testutil.AssertAppError(t, err, http.StatusBadRequest, "error.email_already_verified")
|
||||
}
|
||||
|
||||
func TestAuthService_VerifyEmail_InvalidCode(t *testing.T) {
|
||||
service, _ := setupAuthService(t)
|
||||
|
||||
// Register
|
||||
req := &requests.RegisterRequest{
|
||||
Username: "newuser",
|
||||
Email: "new@test.com",
|
||||
Password: "Password123",
|
||||
}
|
||||
_, _, err := service.Register(req)
|
||||
require.NoError(t, err)
|
||||
|
||||
user, err := service.userRepo.FindByEmail("new@test.com")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Wrong code — with DebugFixedCodes enabled, "123456" bypasses normal lookup,
|
||||
// but a wrong code should use the normal path
|
||||
err = service.VerifyEmail(user.ID, "000000")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
// === ResendVerificationCode ===
|
||||
|
||||
func TestAuthService_ResendVerificationCode(t *testing.T) {
|
||||
service, _ := setupAuthService(t)
|
||||
|
||||
// Register
|
||||
req := &requests.RegisterRequest{
|
||||
Username: "newuser",
|
||||
Email: "new@test.com",
|
||||
Password: "Password123",
|
||||
}
|
||||
_, _, err := service.Register(req)
|
||||
require.NoError(t, err)
|
||||
|
||||
user, err := service.userRepo.FindByEmail("new@test.com")
|
||||
require.NoError(t, err)
|
||||
|
||||
code, err := service.ResendVerificationCode(user.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "123456", code) // DebugFixedCodes
|
||||
}
|
||||
|
||||
func TestAuthService_ResendVerificationCode_AlreadyVerified(t *testing.T) {
|
||||
service, _ := setupAuthService(t)
|
||||
|
||||
// Register and verify
|
||||
req := &requests.RegisterRequest{
|
||||
Username: "newuser",
|
||||
Email: "new@test.com",
|
||||
Password: "Password123",
|
||||
}
|
||||
_, _, err := service.Register(req)
|
||||
require.NoError(t, err)
|
||||
|
||||
user, err := service.userRepo.FindByEmail("new@test.com")
|
||||
require.NoError(t, err)
|
||||
|
||||
err = service.VerifyEmail(user.ID, "123456")
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = service.ResendVerificationCode(user.ID)
|
||||
testutil.AssertAppError(t, err, http.StatusBadRequest, "error.email_already_verified")
|
||||
}
|
||||
|
||||
// === ForgotPassword ===
|
||||
|
||||
func TestAuthService_ForgotPassword(t *testing.T) {
|
||||
service, _ := setupAuthService(t)
|
||||
|
||||
// Register a user first
|
||||
registerReq := &requests.RegisterRequest{
|
||||
Username: "testuser",
|
||||
Email: "test@test.com",
|
||||
Password: "Password123",
|
||||
}
|
||||
_, _, err := service.Register(registerReq)
|
||||
require.NoError(t, err)
|
||||
|
||||
code, user, err := service.ForgotPassword("test@test.com")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "123456", code) // DebugFixedCodes
|
||||
assert.NotNil(t, user)
|
||||
assert.Equal(t, "test@test.com", user.Email)
|
||||
}
|
||||
|
||||
func TestAuthService_ForgotPassword_NonexistentEmail(t *testing.T) {
|
||||
service, _ := setupAuthService(t)
|
||||
|
||||
// Should not reveal that email doesn't exist
|
||||
code, user, err := service.ForgotPassword("nonexistent@test.com")
|
||||
require.NoError(t, err)
|
||||
assert.Empty(t, code)
|
||||
assert.Nil(t, user)
|
||||
}
|
||||
|
||||
// === ResetPassword ===
|
||||
|
||||
func TestAuthService_ResetPassword(t *testing.T) {
|
||||
service, _ := setupAuthService(t)
|
||||
|
||||
// Register
|
||||
registerReq := &requests.RegisterRequest{
|
||||
Username: "testuser",
|
||||
Email: "test@test.com",
|
||||
Password: "Password123",
|
||||
}
|
||||
_, _, err := service.Register(registerReq)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Forgot password
|
||||
_, _, err = service.ForgotPassword("test@test.com")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify reset code to get the token
|
||||
resetToken, err := service.VerifyResetCode("test@test.com", "123456")
|
||||
require.NoError(t, err)
|
||||
assert.NotEmpty(t, resetToken)
|
||||
|
||||
// Reset password
|
||||
err = service.ResetPassword(resetToken, "NewPassword123")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Login with new password
|
||||
loginReq := &requests.LoginRequest{
|
||||
Username: "testuser",
|
||||
Password: "NewPassword123",
|
||||
}
|
||||
loginResp, err := service.Login(loginReq)
|
||||
require.NoError(t, err)
|
||||
assert.NotEmpty(t, loginResp.Token)
|
||||
}
|
||||
|
||||
func TestAuthService_ResetPassword_InvalidToken(t *testing.T) {
|
||||
service, _ := setupAuthService(t)
|
||||
|
||||
err := service.ResetPassword("invalid-token", "NewPassword123")
|
||||
testutil.AssertAppError(t, err, http.StatusBadRequest, "error.invalid_reset_token")
|
||||
}
|
||||
|
||||
// === Logout ===
|
||||
|
||||
func TestAuthService_Logout(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
userRepo := repositories.NewUserRepository(db)
|
||||
cfg := &config.Config{
|
||||
Security: config.SecurityConfig{SecretKey: "test-secret"},
|
||||
}
|
||||
service := NewAuthService(userRepo, cfg)
|
||||
|
||||
user := testutil.CreateTestUser(t, db, "testuser", "test@test.com", "Password123")
|
||||
|
||||
// Login first
|
||||
loginReq := &requests.LoginRequest{
|
||||
Username: "testuser",
|
||||
Password: "Password123",
|
||||
}
|
||||
loginResp, err := service.Login(loginReq)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Logout
|
||||
err = service.Logout(loginResp.Token)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Token should be deleted — refreshing should fail
|
||||
_, err = service.RefreshToken(loginResp.Token, user.ID)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
// === DeleteAccount ===
|
||||
|
||||
func TestAuthService_DeleteAccount_EmailAuth(t *testing.T) {
|
||||
service, _ := setupAuthService(t)
|
||||
|
||||
// Register
|
||||
registerReq := &requests.RegisterRequest{
|
||||
Username: "testuser",
|
||||
Email: "test@test.com",
|
||||
Password: "Password123",
|
||||
}
|
||||
_, _, err := service.Register(registerReq)
|
||||
require.NoError(t, err)
|
||||
|
||||
user, err := service.userRepo.FindByEmail("test@test.com")
|
||||
require.NoError(t, err)
|
||||
|
||||
password := "Password123"
|
||||
_, err = service.DeleteAccount(user.ID, &password, nil)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestAuthService_DeleteAccount_WrongPassword(t *testing.T) {
|
||||
service, _ := setupAuthService(t)
|
||||
|
||||
registerReq := &requests.RegisterRequest{
|
||||
Username: "testuser",
|
||||
Email: "test@test.com",
|
||||
Password: "Password123",
|
||||
}
|
||||
_, _, err := service.Register(registerReq)
|
||||
require.NoError(t, err)
|
||||
|
||||
user, err := service.userRepo.FindByEmail("test@test.com")
|
||||
require.NoError(t, err)
|
||||
|
||||
wrongPassword := "WrongPassword1"
|
||||
_, err = service.DeleteAccount(user.ID, &wrongPassword, nil)
|
||||
testutil.AssertAppError(t, err, http.StatusUnauthorized, "error.invalid_credentials")
|
||||
}
|
||||
|
||||
func TestAuthService_DeleteAccount_NoPassword(t *testing.T) {
|
||||
service, _ := setupAuthService(t)
|
||||
|
||||
registerReq := &requests.RegisterRequest{
|
||||
Username: "testuser",
|
||||
Email: "test@test.com",
|
||||
Password: "Password123",
|
||||
}
|
||||
_, _, err := service.Register(registerReq)
|
||||
require.NoError(t, err)
|
||||
|
||||
user, err := service.userRepo.FindByEmail("test@test.com")
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = service.DeleteAccount(user.ID, nil, nil)
|
||||
testutil.AssertAppError(t, err, http.StatusBadRequest, "error.password_required")
|
||||
}
|
||||
|
||||
func TestAuthService_DeleteAccount_UserNotFound(t *testing.T) {
|
||||
service, _ := setupAuthService(t)
|
||||
|
||||
password := "Password123"
|
||||
_, err := service.DeleteAccount(99999, &password, nil)
|
||||
testutil.AssertAppError(t, err, http.StatusNotFound, "error.user_not_found")
|
||||
}
|
||||
|
||||
// === Helper functions ===
|
||||
|
||||
func TestGenerateSixDigitCode(t *testing.T) {
|
||||
code := generateSixDigitCode()
|
||||
assert.Len(t, code, 6)
|
||||
// Should be numeric
|
||||
for _, c := range code {
|
||||
assert.True(t, c >= '0' && c <= '9', "code should contain only digits")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateResetToken(t *testing.T) {
|
||||
token := generateResetToken()
|
||||
assert.NotEmpty(t, token)
|
||||
assert.Len(t, token, 64) // 32 bytes = 64 hex chars
|
||||
}
|
||||
|
||||
func TestGetStringOrEmpty(t *testing.T) {
|
||||
s := "hello"
|
||||
assert.Equal(t, "hello", getStringOrEmpty(&s))
|
||||
assert.Equal(t, "", getStringOrEmpty(nil))
|
||||
}
|
||||
|
||||
func TestIsPrivateRelayEmail(t *testing.T) {
|
||||
assert.True(t, isPrivateRelayEmail("abc@privaterelay.appleid.com"))
|
||||
assert.True(t, isPrivateRelayEmail("ABC@PRIVATERELAY.APPLEID.COM"))
|
||||
assert.False(t, isPrivateRelayEmail("user@gmail.com"))
|
||||
}
|
||||
|
||||
func TestGetEmailFromRequest(t *testing.T) {
|
||||
email := "req@test.com"
|
||||
assert.Equal(t, "req@test.com", getEmailFromRequest(&email, "claims@test.com"))
|
||||
assert.Equal(t, "claims@test.com", getEmailFromRequest(nil, "claims@test.com"))
|
||||
empty := ""
|
||||
assert.Equal(t, "claims@test.com", getEmailFromRequest(&empty, "claims@test.com"))
|
||||
}
|
||||
|
||||
// === getEmailOrDefault ===
|
||||
|
||||
func TestGetEmailOrDefault(t *testing.T) {
|
||||
// Non-empty email returns itself
|
||||
assert.Equal(t, "user@test.com", getEmailOrDefault("user@test.com"))
|
||||
|
||||
// Empty email returns a generated placeholder
|
||||
result := getEmailOrDefault("")
|
||||
assert.Contains(t, result, "@privaterelay.appleid.com")
|
||||
assert.Contains(t, result, "apple_")
|
||||
}
|
||||
|
||||
// === generateUniqueUsername ===
|
||||
|
||||
func TestGenerateUniqueUsername(t *testing.T) {
|
||||
// Normal email generates username from email prefix
|
||||
username := generateUniqueUsername("john@test.com", nil)
|
||||
assert.Contains(t, username, "john_")
|
||||
|
||||
// Private relay email falls back to first name
|
||||
firstName := "Jane"
|
||||
username = generateUniqueUsername("abc@privaterelay.appleid.com", &firstName)
|
||||
assert.Contains(t, username, "jane_")
|
||||
|
||||
// Private relay email and no first name — fallback
|
||||
username = generateUniqueUsername("abc@privaterelay.appleid.com", nil)
|
||||
assert.Contains(t, username, "user_")
|
||||
|
||||
// Empty email with first name
|
||||
firstName2 := "Bob"
|
||||
username = generateUniqueUsername("", &firstName2)
|
||||
assert.Contains(t, username, "bob_")
|
||||
|
||||
// Empty email and no first name
|
||||
username = generateUniqueUsername("", nil)
|
||||
assert.Contains(t, username, "user_")
|
||||
}
|
||||
|
||||
// === generateGoogleUsername ===
|
||||
|
||||
func TestGenerateGoogleUsername(t *testing.T) {
|
||||
// Normal email
|
||||
username := generateGoogleUsername("john@gmail.com", "John")
|
||||
assert.Contains(t, username, "john_")
|
||||
|
||||
// Empty email falls back to first name
|
||||
username = generateGoogleUsername("", "Alice")
|
||||
assert.Contains(t, username, "alice_")
|
||||
|
||||
// Empty email and empty first name — fallback
|
||||
username = generateGoogleUsername("", "")
|
||||
assert.Contains(t, username, "google_")
|
||||
}
|
||||
|
||||
// === Login with empty password ===
|
||||
|
||||
func TestAuthService_Login_EmptyPassword(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
userRepo := repositories.NewUserRepository(db)
|
||||
cfg := &config.Config{
|
||||
Security: config.SecurityConfig{SecretKey: "test-secret"},
|
||||
}
|
||||
service := NewAuthService(userRepo, cfg)
|
||||
|
||||
testutil.CreateTestUser(t, db, "testuser", "test@test.com", "Password123")
|
||||
|
||||
req := &requests.LoginRequest{
|
||||
Username: "testuser",
|
||||
Password: "",
|
||||
}
|
||||
|
||||
_, err := service.Login(req)
|
||||
testutil.AssertAppError(t, err, http.StatusUnauthorized, "error.invalid_credentials")
|
||||
}
|
||||
|
||||
// === ForgotPassword rate limiting ===
|
||||
|
||||
func TestAuthService_ForgotPassword_RateLimit(t *testing.T) {
|
||||
service, _ := setupAuthService(t)
|
||||
|
||||
registerReq := &requests.RegisterRequest{
|
||||
Username: "testuser",
|
||||
Email: "test@test.com",
|
||||
Password: "Password123",
|
||||
}
|
||||
_, _, err := service.Register(registerReq)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Make max allowed reset requests (3 based on setup)
|
||||
for i := 0; i < 3; i++ {
|
||||
_, _, err := service.ForgotPassword("test@test.com")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// The 4th should be rate limited
|
||||
_, _, err = service.ForgotPassword("test@test.com")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
// === VerifyResetCode with wrong code ===
|
||||
|
||||
func TestAuthService_VerifyResetCode_WrongCode(t *testing.T) {
|
||||
service, _ := setupAuthService(t)
|
||||
|
||||
registerReq := &requests.RegisterRequest{
|
||||
Username: "testuser",
|
||||
Email: "test@test.com",
|
||||
Password: "Password123",
|
||||
}
|
||||
_, _, err := service.Register(registerReq)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, _, err = service.ForgotPassword("test@test.com")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Wrong code but with debug mode, "123456" works, "000000" should fail
|
||||
_, err = service.VerifyResetCode("test@test.com", "000000")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
// === VerifyResetCode with nonexistent email ===
|
||||
|
||||
func TestAuthService_VerifyResetCode_NonexistentEmail(t *testing.T) {
|
||||
service, _ := setupAuthService(t)
|
||||
|
||||
_, err := service.VerifyResetCode("nonexistent@test.com", "123456")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
// === UpdateProfile — change email to new email ===
|
||||
|
||||
func TestAuthService_UpdateProfile_ChangeEmail(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
userRepo := repositories.NewUserRepository(db)
|
||||
cfg := &config.Config{
|
||||
Security: config.SecurityConfig{SecretKey: "test-secret"},
|
||||
}
|
||||
service := NewAuthService(userRepo, cfg)
|
||||
|
||||
user := testutil.CreateTestUser(t, db, "testuser", "test@test.com", "Password123")
|
||||
userRepo.GetOrCreateProfile(user.ID)
|
||||
|
||||
newEmail := "newemail@test.com"
|
||||
req := &requests.UpdateProfileRequest{
|
||||
Email: &newEmail,
|
||||
}
|
||||
|
||||
resp, err := service.UpdateProfile(user.ID, req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "newemail@test.com", resp.Email)
|
||||
}
|
||||
|
||||
// === DeleteAccount — empty password string ===
|
||||
|
||||
func TestAuthService_DeleteAccount_EmptyPassword(t *testing.T) {
|
||||
service, _ := setupAuthService(t)
|
||||
|
||||
registerReq := &requests.RegisterRequest{
|
||||
Username: "testuser",
|
||||
Email: "test@test.com",
|
||||
Password: "Password123",
|
||||
}
|
||||
_, _, err := service.Register(registerReq)
|
||||
require.NoError(t, err)
|
||||
|
||||
user, err := service.userRepo.FindByEmail("test@test.com")
|
||||
require.NoError(t, err)
|
||||
|
||||
emptyPw := ""
|
||||
_, err = service.DeleteAccount(user.ID, &emptyPw, nil)
|
||||
testutil.AssertAppError(t, err, http.StatusBadRequest, "error.password_required")
|
||||
}
|
||||
|
||||
// === SetNotificationRepository ===
|
||||
|
||||
func TestAuthService_SetNotificationRepository(t *testing.T) {
|
||||
db := testutil.SetupTestDB(t)
|
||||
userRepo := repositories.NewUserRepository(db)
|
||||
notifRepo := repositories.NewNotificationRepository(db)
|
||||
cfg := &config.Config{
|
||||
Security: config.SecurityConfig{SecretKey: "test-secret"},
|
||||
}
|
||||
service := NewAuthService(userRepo, cfg)
|
||||
assert.Nil(t, service.notificationRepo)
|
||||
|
||||
service.SetNotificationRepository(notifRepo)
|
||||
assert.NotNil(t, service.notificationRepo)
|
||||
}
|
||||
|
||||
// === Register creates profile and notification preferences ===
|
||||
|
||||
func TestAuthService_Register_CreatesProfile(t *testing.T) {
|
||||
service, userRepo := setupAuthService(t)
|
||||
|
||||
req := &requests.RegisterRequest{
|
||||
Username: "profileuser",
|
||||
Email: "profile@test.com",
|
||||
Password: "Password123",
|
||||
FirstName: "John",
|
||||
LastName: "Doe",
|
||||
}
|
||||
|
||||
resp, _, err := service.Register(req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "profileuser", resp.User.Username)
|
||||
|
||||
// Profile should exist
|
||||
profile, err := userRepo.GetOrCreateProfile(resp.User.ID)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, profile)
|
||||
}
|
||||
Reference in New Issue
Block a user