- 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>
218 lines
6.5 KiB
Go
218 lines
6.5 KiB
Go
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)
|
|
})
|
|
}
|