veza/veza-backend-api/internal/handlers/profile_handler_test.go
senke 97069a2bf4 [BE-TEST-007] test: Add unit tests for webhook handlers
- Added comprehensive unit tests for all webhook handler methods:
  * RegisterWebhook (success, invalid URL, no events, unauthorized)
  * ListWebhooks (success)
  * DeleteWebhook (success, not found, invalid ID)
  * GetWebhookStats (success)
  * TestWebhook (success, not found)
  * RegenerateAPIKey (success, not found, invalid ID)
- Fixed validation bug in BindAndValidateJSON to properly return errors for binding validation failures
- Fixed compilation errors in profile_handler_test.go and room_handler_test.go
- All tests passing
2025-12-25 01:32:54 +01:00

603 lines
18 KiB
Go

package handlers
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"testing"
"time"
"veza-backend-api/internal/database"
"veza-backend-api/internal/models"
"veza-backend-api/internal/repositories"
"veza-backend-api/internal/services"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
// setupTestProfileHandler creates a test handler with real services and in-memory database
func setupTestProfileHandler(t *testing.T) (*ProfileHandler, *gorm.DB, *gin.Engine, func()) {
gin.SetMode(gin.TestMode)
logger := zaptest.NewLogger(t)
// Setup in-memory SQLite database
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
require.NoError(t, err)
db.Exec("PRAGMA foreign_keys = ON")
// Auto-migrate models
err = db.AutoMigrate(
&models.User{},
&models.Role{},
&models.Permission{},
&models.UserRole{},
&models.RolePermission{},
)
require.NoError(t, err)
// Create follows and user_blocks tables for SocialService (uses raw SQL)
sqlDB, err := db.DB()
require.NoError(t, err)
_, err = sqlDB.Exec(`
CREATE TABLE IF NOT EXISTS follows (
id TEXT PRIMARY KEY,
follower_id TEXT NOT NULL,
followed_id TEXT NOT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE(follower_id, followed_id)
)
`)
require.NoError(t, err)
_, err = sqlDB.Exec(`
CREATE TABLE IF NOT EXISTS user_blocks (
id TEXT PRIMARY KEY,
blocker_id TEXT NOT NULL,
blocked_id TEXT NOT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE(blocker_id, blocked_id)
)
`)
require.NoError(t, err)
// Setup repositories
userRepo := repositories.NewGormUserRepository(db)
// Setup services
userService := services.NewUserServiceWithDB(userRepo, db)
permissionService := services.NewPermissionService(db)
// Create database wrapper for SocialService
// Note: SocialService uses raw SQL, so we need to get the underlying sql.DB
sqlDB, err = db.DB()
require.NoError(t, err)
dbWrapper := &database.Database{}
dbWrapper.DB = sqlDB
dbWrapper.GormDB = db
socialService := services.NewSocialService(dbWrapper, logger)
handler := NewProfileHandler(userService, logger)
handler.SetSocialService(socialService)
handler.SetPermissionService(permissionService)
router := gin.New()
router.Use(func(c *gin.Context) {
// Mock auth middleware - set user_id from header if present
userIDStr := c.GetHeader("X-User-ID")
if userIDStr != "" {
uid, err := uuid.Parse(userIDStr)
if err == nil {
c.Set("user_id", uid)
}
}
c.Next()
})
cleanup := func() {
// Database cleanup handled by test
}
return handler, db, router, cleanup
}
// Helper to create a test user
func createTestUserForProfile(id uuid.UUID, username string) *models.User {
return &models.User{
ID: id,
Username: username,
Email: fmt.Sprintf("%s@example.com", username),
IsActive: true,
IsVerified: true,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
}
// TestProfileHandler_GetProfile_Success tests successful profile retrieval
func TestProfileHandler_GetProfile_Success(t *testing.T) {
handler, db, router, cleanup := setupTestProfileHandler(t)
defer cleanup()
// Create test user
userID := uuid.New()
user := createTestUserForProfile(userID, "testuser")
err := db.Create(user).Error
require.NoError(t, err)
router.GET("/users/:id", handler.GetProfile)
req := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/users/%s", userID.String()), nil)
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
var response map[string]interface{}
err = json.Unmarshal(w.Body.Bytes(), &response)
require.NoError(t, err)
assert.True(t, response["success"].(bool))
}
// TestProfileHandler_GetProfile_NotFound tests profile not found scenario
func TestProfileHandler_GetProfile_NotFound(t *testing.T) {
handler, _, router, cleanup := setupTestProfileHandler(t)
defer cleanup()
router.GET("/users/:id", handler.GetProfile)
nonExistentID := uuid.New()
req := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/users/%s", nonExistentID.String()), nil)
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusNotFound, w.Code)
}
// TestProfileHandler_GetProfile_InvalidID tests invalid user ID format
func TestProfileHandler_GetProfile_InvalidID(t *testing.T) {
handler, _, router, cleanup := setupTestProfileHandler(t)
defer cleanup()
router.GET("/users/:id", handler.GetProfile)
req := httptest.NewRequest(http.MethodGet, "/users/invalid-id", nil)
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusBadRequest, w.Code)
}
// TestProfileHandler_GetProfileByUsername_Success tests successful profile retrieval by username
func TestProfileHandler_GetProfileByUsername_Success(t *testing.T) {
handler, db, router, cleanup := setupTestProfileHandler(t)
defer cleanup()
// Create test user
userID := uuid.New()
user := createTestUserForProfile(userID, "testuser")
err := db.Create(user).Error
require.NoError(t, err)
router.GET("/users/by-username/:username", handler.GetProfileByUsername)
req := httptest.NewRequest(http.MethodGet, "/users/by-username/testuser", nil)
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
var response map[string]interface{}
err = json.Unmarshal(w.Body.Bytes(), &response)
require.NoError(t, err)
assert.True(t, response["success"].(bool))
}
// TestProfileHandler_GetProfileByUsername_NotFound tests username not found scenario
func TestProfileHandler_GetProfileByUsername_NotFound(t *testing.T) {
handler, _, router, cleanup := setupTestProfileHandler(t)
defer cleanup()
router.GET("/users/by-username/:username", handler.GetProfileByUsername)
req := httptest.NewRequest(http.MethodGet, "/users/by-username/nonexistent", nil)
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusNotFound, w.Code)
}
// TestProfileHandler_ListUsers_Success tests successful user listing
func TestProfileHandler_ListUsers_Success(t *testing.T) {
handler, db, router, cleanup := setupTestProfileHandler(t)
defer cleanup()
// Create test users
for i := 0; i < 3; i++ {
user := createTestUserForProfile(uuid.New(), fmt.Sprintf("user%d", i+1))
err := db.Create(user).Error
require.NoError(t, err)
}
router.GET("/users", handler.ListUsers)
req := httptest.NewRequest(http.MethodGet, "/users?page=1&limit=10", nil)
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
var response map[string]interface{}
err := json.Unmarshal(w.Body.Bytes(), &response)
require.NoError(t, err)
assert.True(t, response["success"].(bool))
}
// TestProfileHandler_SearchUsers_Success tests successful user search
func TestProfileHandler_SearchUsers_Success(t *testing.T) {
handler, db, router, cleanup := setupTestProfileHandler(t)
defer cleanup()
// Create test user
userID := uuid.New()
user := createTestUserForProfile(userID, "testuser")
err := db.Create(user).Error
require.NoError(t, err)
router.GET("/users/search", handler.SearchUsers)
req := httptest.NewRequest(http.MethodGet, "/users/search?q=test&page=1&limit=10", nil)
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
var response map[string]interface{}
err = json.Unmarshal(w.Body.Bytes(), &response)
require.NoError(t, err)
assert.True(t, response["success"].(bool))
}
// TestProfileHandler_UpdateProfile_Success tests successful profile update
func TestProfileHandler_UpdateProfile_Success(t *testing.T) {
handler, db, router, cleanup := setupTestProfileHandler(t)
defer cleanup()
// Create test user
userID := uuid.New()
user := createTestUserForProfile(userID, "testuser")
err := db.Create(user).Error
require.NoError(t, err)
router.PUT("/users/:id", handler.UpdateProfile)
updateReq := UpdateProfileRequest{
FirstName: "John",
LastName: "Doe",
Bio: "Test bio",
}
body, _ := json.Marshal(updateReq)
req := httptest.NewRequest(http.MethodPut, fmt.Sprintf("/users/%s", userID.String()), bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-User-ID", userID.String())
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
var response map[string]interface{}
err = json.Unmarshal(w.Body.Bytes(), &response)
require.NoError(t, err)
assert.True(t, response["success"].(bool))
}
// TestProfileHandler_UpdateProfile_Forbidden tests forbidden profile update (different user)
func TestProfileHandler_UpdateProfile_Forbidden(t *testing.T) {
handler, db, router, cleanup := setupTestProfileHandler(t)
defer cleanup()
// Create test users
userID1 := uuid.New()
user1 := createTestUserForProfile(userID1, "user1")
err := db.Create(user1).Error
require.NoError(t, err)
userID2 := uuid.New()
user2 := createTestUserForProfile(userID2, "user2")
err = db.Create(user2).Error
require.NoError(t, err)
router.PUT("/users/:id", handler.UpdateProfile)
updateReq := UpdateProfileRequest{
FirstName: "John",
}
body, _ := json.Marshal(updateReq)
// Try to update user1's profile as user2
req := httptest.NewRequest(http.MethodPut, fmt.Sprintf("/users/%s", userID1.String()), bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-User-ID", userID2.String())
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusForbidden, w.Code)
}
// TestProfileHandler_GetProfileCompletion_Success tests successful profile completion retrieval
func TestProfileHandler_GetProfileCompletion_Success(t *testing.T) {
handler, db, router, cleanup := setupTestProfileHandler(t)
defer cleanup()
// Create test user
userID := uuid.New()
user := createTestUserForProfile(userID, "testuser")
err := db.Create(user).Error
require.NoError(t, err)
router.GET("/users/:id/completion", handler.GetProfileCompletion)
req := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/users/%s/completion", userID.String()), nil)
req.Header.Set("X-User-ID", userID.String())
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
var response map[string]interface{}
err = json.Unmarshal(w.Body.Bytes(), &response)
require.NoError(t, err)
assert.True(t, response["success"].(bool))
}
// TestProfileHandler_GetProfileCompletion_Forbidden tests forbidden access to other user's completion
func TestProfileHandler_GetProfileCompletion_Forbidden(t *testing.T) {
handler, db, router, cleanup := setupTestProfileHandler(t)
defer cleanup()
// Create test users
userID1 := uuid.New()
user1 := createTestUserForProfile(userID1, "user1")
err := db.Create(user1).Error
require.NoError(t, err)
userID2 := uuid.New()
user2 := createTestUserForProfile(userID2, "user2")
err = db.Create(user2).Error
require.NoError(t, err)
router.GET("/users/:id/completion", handler.GetProfileCompletion)
// Try to access user1's completion as user2
req := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/users/%s/completion", userID1.String()), nil)
req.Header.Set("X-User-ID", userID2.String())
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusForbidden, w.Code)
}
// TestProfileHandler_FollowUser_Success tests successful user follow
func TestProfileHandler_FollowUser_Success(t *testing.T) {
handler, db, router, cleanup := setupTestProfileHandler(t)
defer cleanup()
// Create test users
followerID := uuid.New()
follower := createTestUserForProfile(followerID, "follower")
err := db.Create(follower).Error
require.NoError(t, err)
followedID := uuid.New()
followed := createTestUserForProfile(followedID, "followed")
err = db.Create(followed).Error
require.NoError(t, err)
router.POST("/users/:id/follow", handler.FollowUser)
req := httptest.NewRequest(http.MethodPost, fmt.Sprintf("/users/%s/follow", followedID.String()), nil)
req.Header.Set("X-User-ID", followerID.String())
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
var response map[string]interface{}
err = json.Unmarshal(w.Body.Bytes(), &response)
require.NoError(t, err)
assert.True(t, response["success"].(bool))
}
// TestProfileHandler_FollowUser_CannotFollowSelf tests that user cannot follow themselves
func TestProfileHandler_FollowUser_CannotFollowSelf(t *testing.T) {
handler, db, router, cleanup := setupTestProfileHandler(t)
defer cleanup()
// Create test user
userID := uuid.New()
user := createTestUserForProfile(userID, "testuser")
err := db.Create(user).Error
require.NoError(t, err)
router.POST("/users/:id/follow", handler.FollowUser)
req := httptest.NewRequest(http.MethodPost, fmt.Sprintf("/users/%s/follow", userID.String()), nil)
req.Header.Set("X-User-ID", userID.String())
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusBadRequest, w.Code)
}
// TestProfileHandler_UnfollowUser_Success tests successful user unfollow
func TestProfileHandler_UnfollowUser_Success(t *testing.T) {
handler, db, router, cleanup := setupTestProfileHandler(t)
defer cleanup()
// Create test users
followerID := uuid.New()
follower := createTestUserForProfile(followerID, "follower")
err := db.Create(follower).Error
require.NoError(t, err)
followedID := uuid.New()
followed := createTestUserForProfile(followedID, "followed")
err = db.Create(followed).Error
require.NoError(t, err)
// Create follow relationship using raw SQL (follows table)
sqlDB, err := db.DB()
require.NoError(t, err)
_, err = sqlDB.Exec("INSERT INTO follows (follower_id, followed_id) VALUES (?, ?)", followerID, followedID)
require.NoError(t, err)
router.DELETE("/users/:id/follow", handler.UnfollowUser)
req := httptest.NewRequest(http.MethodDelete, fmt.Sprintf("/users/%s/follow", followedID.String()), nil)
req.Header.Set("X-User-ID", followerID.String())
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
var response map[string]interface{}
err = json.Unmarshal(w.Body.Bytes(), &response)
require.NoError(t, err)
assert.True(t, response["success"].(bool))
}
// TestProfileHandler_BlockUser_Success tests successful user block
func TestProfileHandler_BlockUser_Success(t *testing.T) {
handler, db, router, cleanup := setupTestProfileHandler(t)
defer cleanup()
// Create test users
blockerID := uuid.New()
blocker := createTestUserForProfile(blockerID, "blocker")
err := db.Create(blocker).Error
require.NoError(t, err)
blockedID := uuid.New()
blocked := createTestUserForProfile(blockedID, "blocked")
err = db.Create(blocked).Error
require.NoError(t, err)
router.POST("/users/:id/block", handler.BlockUser)
req := httptest.NewRequest(http.MethodPost, fmt.Sprintf("/users/%s/block", blockedID.String()), nil)
req.Header.Set("X-User-ID", blockerID.String())
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
var response map[string]interface{}
err = json.Unmarshal(w.Body.Bytes(), &response)
require.NoError(t, err)
assert.True(t, response["success"].(bool))
}
// TestProfileHandler_BlockUser_CannotBlockSelf tests that user cannot block themselves
func TestProfileHandler_BlockUser_CannotBlockSelf(t *testing.T) {
handler, db, router, cleanup := setupTestProfileHandler(t)
defer cleanup()
// Create test user
userID := uuid.New()
user := createTestUserForProfile(userID, "testuser")
err := db.Create(user).Error
require.NoError(t, err)
router.POST("/users/:id/block", handler.BlockUser)
req := httptest.NewRequest(http.MethodPost, fmt.Sprintf("/users/%s/block", userID.String()), nil)
req.Header.Set("X-User-ID", userID.String())
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusBadRequest, w.Code)
}
// TestProfileHandler_UnblockUser_Success tests successful user unblock
func TestProfileHandler_UnblockUser_Success(t *testing.T) {
handler, db, router, cleanup := setupTestProfileHandler(t)
defer cleanup()
// Create test users
blockerID := uuid.New()
blocker := createTestUserForProfile(blockerID, "blocker")
err := db.Create(blocker).Error
require.NoError(t, err)
blockedID := uuid.New()
blocked := createTestUserForProfile(blockedID, "blocked")
err = db.Create(blocked).Error
require.NoError(t, err)
// Create block relationship using raw SQL (user_blocks table)
sqlDB, err := db.DB()
require.NoError(t, err)
_, err = sqlDB.Exec("INSERT INTO user_blocks (blocker_id, blocked_id) VALUES (?, ?)", blockerID, blockedID)
require.NoError(t, err)
router.DELETE("/users/:id/block", handler.UnblockUser)
req := httptest.NewRequest(http.MethodDelete, fmt.Sprintf("/users/%s/block", blockedID.String()), nil)
req.Header.Set("X-User-ID", blockerID.String())
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
var response map[string]interface{}
err = json.Unmarshal(w.Body.Bytes(), &response)
require.NoError(t, err)
assert.True(t, response["success"].(bool))
}
// TestProfileHandler_DeleteUser_Success tests successful user deletion
func TestProfileHandler_DeleteUser_Success(t *testing.T) {
handler, db, router, cleanup := setupTestProfileHandler(t)
defer cleanup()
// Create test user
userID := uuid.New()
user := createTestUserForProfile(userID, "testuser")
err := db.Create(user).Error
require.NoError(t, err)
router.DELETE("/users/:id", handler.DeleteUser)
req := httptest.NewRequest(http.MethodDelete, fmt.Sprintf("/users/%s", userID.String()), nil)
req.Header.Set("X-User-ID", userID.String())
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
var response map[string]interface{}
err = json.Unmarshal(w.Body.Bytes(), &response)
require.NoError(t, err)
assert.True(t, response["success"].(bool))
}
// TestProfileHandler_DeleteUser_Forbidden tests forbidden user deletion (different user)
func TestProfileHandler_DeleteUser_Forbidden(t *testing.T) {
handler, db, router, cleanup := setupTestProfileHandler(t)
defer cleanup()
// Create test users
userID1 := uuid.New()
user1 := createTestUserForProfile(userID1, "user1")
err := db.Create(user1).Error
require.NoError(t, err)
userID2 := uuid.New()
user2 := createTestUserForProfile(userID2, "user2")
err = db.Create(user2).Error
require.NoError(t, err)
router.DELETE("/users/:id", handler.DeleteUser)
// Try to delete user1 as user2
req := httptest.NewRequest(http.MethodDelete, fmt.Sprintf("/users/%s", userID1.String()), nil)
req.Header.Set("X-User-ID", userID2.String())
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusForbidden, w.Code)
}