package handlers import ( "encoding/json" "net/http" "testing" "time" "github.com/labstack/echo/v4" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "gorm.io/gorm" "github.com/treytartt/honeydue-api/internal/config" "github.com/treytartt/honeydue-api/internal/models" "github.com/treytartt/honeydue-api/internal/repositories" "github.com/treytartt/honeydue-api/internal/services" "github.com/treytartt/honeydue-api/internal/testutil" ) func setupDeleteAccountHandler(t *testing.T) (*AuthHandler, *echo.Echo, *gorm.DB) { db := testutil.SetupTestDB(t) userRepo := repositories.NewUserRepository(db) cfg := &config.Config{ Security: config.SecurityConfig{ SecretKey: "test-secret-key", PasswordResetExpiry: 15 * time.Minute, ConfirmationExpiry: 24 * time.Hour, MaxPasswordResetRate: 3, }, } authService := services.NewAuthService(userRepo, cfg) handler := NewAuthHandler(authService, nil, nil) e := testutil.SetupTestRouter() return handler, e, db } func TestAuthHandler_DeleteAccount_EmailUser(t *testing.T) { handler, e, db := setupDeleteAccountHandler(t) user := testutil.CreateTestUser(t, db, "deletetest", "delete@test.com", "password123") // Create profile for the user profile := &models.UserProfile{UserID: user.ID, Verified: true} require.NoError(t, db.Create(profile).Error) // Create auth token testutil.CreateTestToken(t, db, user.ID) authGroup := e.Group("/api/auth") authGroup.Use(testutil.MockAuthMiddleware(user)) authGroup.DELETE("/account/", handler.DeleteAccount) t.Run("successful deletion with correct password", func(t *testing.T) { password := "password123" req := map[string]interface{}{ "password": password, } w := testutil.MakeRequest(e, "DELETE", "/api/auth/account/", req, "test-token") testutil.AssertStatusCode(t, w, http.StatusOK) var response map[string]interface{} err := json.Unmarshal(w.Body.Bytes(), &response) require.NoError(t, err) assert.Contains(t, response["message"], "Account deleted successfully") // Verify user is actually deleted var count int64 db.Model(&models.User{}).Where("id = ?", user.ID).Count(&count) assert.Equal(t, int64(0), count) // Verify profile is deleted db.Model(&models.UserProfile{}).Where("user_id = ?", user.ID).Count(&count) assert.Equal(t, int64(0), count) // Verify auth token is deleted db.Model(&models.AuthToken{}).Where("user_id = ?", user.ID).Count(&count) assert.Equal(t, int64(0), count) }) } func TestAuthHandler_DeleteAccount_WrongPassword(t *testing.T) { handler, e, db := setupDeleteAccountHandler(t) user := testutil.CreateTestUser(t, db, "wrongpw", "wrongpw@test.com", "password123") authGroup := e.Group("/api/auth") authGroup.Use(testutil.MockAuthMiddleware(user)) authGroup.DELETE("/account/", handler.DeleteAccount) t.Run("wrong password returns 401", func(t *testing.T) { wrongPw := "wrongpassword" req := map[string]interface{}{ "password": wrongPw, } w := testutil.MakeRequest(e, "DELETE", "/api/auth/account/", req, "test-token") testutil.AssertStatusCode(t, w, http.StatusUnauthorized) }) } func TestAuthHandler_DeleteAccount_MissingPassword(t *testing.T) { handler, e, db := setupDeleteAccountHandler(t) user := testutil.CreateTestUser(t, db, "nopw", "nopw@test.com", "password123") authGroup := e.Group("/api/auth") authGroup.Use(testutil.MockAuthMiddleware(user)) authGroup.DELETE("/account/", handler.DeleteAccount) t.Run("missing password returns 400", func(t *testing.T) { req := map[string]interface{}{} w := testutil.MakeRequest(e, "DELETE", "/api/auth/account/", req, "test-token") testutil.AssertStatusCode(t, w, http.StatusBadRequest) }) } func TestAuthHandler_DeleteAccount_SocialAuthUser(t *testing.T) { handler, e, db := setupDeleteAccountHandler(t) user := testutil.CreateTestUser(t, db, "appleuser", "apple@test.com", "randompassword") // Create Apple social auth record appleAuth := &models.AppleSocialAuth{ UserID: user.ID, AppleID: "apple_sub_123", Email: "apple@test.com", } require.NoError(t, db.Create(appleAuth).Error) // Create profile profile := &models.UserProfile{UserID: user.ID, Verified: true} require.NoError(t, db.Create(profile).Error) authGroup := e.Group("/api/auth") authGroup.Use(testutil.MockAuthMiddleware(user)) authGroup.DELETE("/account/", handler.DeleteAccount) t.Run("successful deletion with DELETE confirmation", func(t *testing.T) { confirmation := "DELETE" req := map[string]interface{}{ "confirmation": confirmation, } w := testutil.MakeRequest(e, "DELETE", "/api/auth/account/", req, "test-token") testutil.AssertStatusCode(t, w, http.StatusOK) // Verify user is deleted var count int64 db.Model(&models.User{}).Where("id = ?", user.ID).Count(&count) assert.Equal(t, int64(0), count) // Verify apple auth is deleted db.Model(&models.AppleSocialAuth{}).Where("user_id = ?", user.ID).Count(&count) assert.Equal(t, int64(0), count) }) } func TestAuthHandler_DeleteAccount_SocialAuthMissingConfirmation(t *testing.T) { handler, e, db := setupDeleteAccountHandler(t) user := testutil.CreateTestUser(t, db, "googleuser", "google@test.com", "randompassword") // Create Google social auth record googleAuth := &models.GoogleSocialAuth{ UserID: user.ID, GoogleID: "google_sub_456", Email: "google@test.com", } require.NoError(t, db.Create(googleAuth).Error) authGroup := e.Group("/api/auth") authGroup.Use(testutil.MockAuthMiddleware(user)) authGroup.DELETE("/account/", handler.DeleteAccount) t.Run("missing confirmation returns 400", func(t *testing.T) { req := map[string]interface{}{} w := testutil.MakeRequest(e, "DELETE", "/api/auth/account/", req, "test-token") testutil.AssertStatusCode(t, w, http.StatusBadRequest) }) t.Run("wrong confirmation returns 400", func(t *testing.T) { wrongConfirmation := "delete" req := map[string]interface{}{ "confirmation": wrongConfirmation, } w := testutil.MakeRequest(e, "DELETE", "/api/auth/account/", req, "test-token") testutil.AssertStatusCode(t, w, http.StatusBadRequest) }) } func TestAuthHandler_DeleteAccount_Unauthenticated(t *testing.T) { handler, e, _ := setupDeleteAccountHandler(t) // No auth middleware - unauthenticated request e.DELETE("/api/auth/account/", handler.DeleteAccount) t.Run("unauthenticated request returns 401", func(t *testing.T) { req := map[string]interface{}{ "password": "password123", } w := testutil.MakeRequest(e, "DELETE", "/api/auth/account/", req, "") testutil.AssertStatusCode(t, w, http.StatusUnauthorized) }) }