veza/veza-backend-api/internal/handlers/webhook_handlers_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

487 lines
14 KiB
Go

package handlers
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"testing"
"time"
"veza-backend-api/internal/models"
"veza-backend-api/internal/services"
"veza-backend-api/internal/workers"
"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"
)
// setupTestWebhookHandler creates a test handler with real services and in-memory database
func setupTestWebhookHandler(t *testing.T) (*WebhookHandler, *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.Webhook{},
)
require.NoError(t, err)
// Setup webhook service
jwtSecret := "test-secret-key"
webhookService := services.NewWebhookService(db, logger, jwtSecret)
// Setup webhook worker
webhookWorker := workers.NewWebhookWorker(
db,
webhookService,
logger,
100, // Queue size
5, // Workers
3, // Max retries
)
handler := NewWebhookHandler(webhookService, webhookWorker, logger)
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 createTestUserForWebhook(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(),
}
}
// TestWebhookHandler_RegisterWebhook_Success tests successful webhook registration
func TestWebhookHandler_RegisterWebhook_Success(t *testing.T) {
handler, db, router, cleanup := setupTestWebhookHandler(t)
defer cleanup()
// Create test user
userID := uuid.New()
user := createTestUserForWebhook(userID, "testuser")
err := db.Create(user).Error
require.NoError(t, err)
router.POST("/webhooks", handler.RegisterWebhook())
registerReq := map[string]interface{}{
"url": "https://example.com/webhook",
"events": []string{"track.created", "track.updated"},
}
body, _ := json.Marshal(registerReq)
req := httptest.NewRequest(http.MethodPost, "/webhooks", 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.StatusCreated, w.Code)
var response APIResponse
err = json.Unmarshal(w.Body.Bytes(), &response)
require.NoError(t, err)
assert.True(t, response.Success)
}
// TestWebhookHandler_RegisterWebhook_InvalidURL tests webhook registration with invalid URL
func TestWebhookHandler_RegisterWebhook_InvalidURL(t *testing.T) {
handler, db, router, cleanup := setupTestWebhookHandler(t)
defer cleanup()
// Create test user
userID := uuid.New()
user := createTestUserForWebhook(userID, "testuser")
err := db.Create(user).Error
require.NoError(t, err)
router.POST("/webhooks", handler.RegisterWebhook())
registerReq := map[string]interface{}{
"url": "not-a-valid-url",
"events": []string{"track.created"},
}
body, _ := json.Marshal(registerReq)
req := httptest.NewRequest(http.MethodPost, "/webhooks", 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.StatusBadRequest, w.Code)
}
// TestWebhookHandler_RegisterWebhook_NoEvents tests webhook registration with no events
func TestWebhookHandler_RegisterWebhook_NoEvents(t *testing.T) {
handler, db, router, cleanup := setupTestWebhookHandler(t)
defer cleanup()
// Create test user
userID := uuid.New()
user := createTestUserForWebhook(userID, "testuser")
err := db.Create(user).Error
require.NoError(t, err)
router.POST("/webhooks", handler.RegisterWebhook())
registerReq := map[string]interface{}{
"url": "https://example.com/webhook",
"events": []string{},
}
body, _ := json.Marshal(registerReq)
req := httptest.NewRequest(http.MethodPost, "/webhooks", 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.StatusBadRequest, w.Code)
}
// TestWebhookHandler_RegisterWebhook_Unauthorized tests webhook registration without authentication
func TestWebhookHandler_RegisterWebhook_Unauthorized(t *testing.T) {
handler, _, router, cleanup := setupTestWebhookHandler(t)
defer cleanup()
router.POST("/webhooks", handler.RegisterWebhook())
registerReq := map[string]interface{}{
"url": "https://example.com/webhook",
"events": []string{"track.created"},
}
body, _ := json.Marshal(registerReq)
req := httptest.NewRequest(http.MethodPost, "/webhooks", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
// No X-User-ID header
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusUnauthorized, w.Code)
}
// TestWebhookHandler_ListWebhooks_Success tests successful webhook listing
func TestWebhookHandler_ListWebhooks_Success(t *testing.T) {
handler, db, router, cleanup := setupTestWebhookHandler(t)
defer cleanup()
// Create test user
userID := uuid.New()
user := createTestUserForWebhook(userID, "testuser")
err := db.Create(user).Error
require.NoError(t, err)
// Create test webhooks with unique API keys
logger := zaptest.NewLogger(t)
webhookService := services.NewWebhookService(db, logger, "test-secret")
for i := 0; i < 3; i++ {
apiKey, err := webhookService.GenerateAPIKey()
require.NoError(t, err)
webhook := &models.Webhook{
ID: uuid.New(),
UserID: userID,
URL: fmt.Sprintf("https://example.com/webhook%d", i+1),
Events: []string{"track.created"},
Active: true,
APIKey: apiKey,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err = db.Create(webhook).Error
require.NoError(t, err)
}
router.GET("/webhooks", handler.ListWebhooks())
req := httptest.NewRequest(http.MethodGet, "/webhooks", 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 APIResponse
err = json.Unmarshal(w.Body.Bytes(), &response)
require.NoError(t, err)
assert.True(t, response.Success)
}
// TestWebhookHandler_DeleteWebhook_Success tests successful webhook deletion
func TestWebhookHandler_DeleteWebhook_Success(t *testing.T) {
handler, db, router, cleanup := setupTestWebhookHandler(t)
defer cleanup()
// Create test user
userID := uuid.New()
user := createTestUserForWebhook(userID, "testuser")
err := db.Create(user).Error
require.NoError(t, err)
// Create test webhook
webhookID := uuid.New()
webhook := &models.Webhook{
ID: webhookID,
UserID: userID,
URL: "https://example.com/webhook",
Events: []string{"track.created"},
Active: true,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err = db.Create(webhook).Error
require.NoError(t, err)
router.DELETE("/webhooks/:id", handler.DeleteWebhook())
req := httptest.NewRequest(http.MethodDelete, fmt.Sprintf("/webhooks/%s", webhookID.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 APIResponse
err = json.Unmarshal(w.Body.Bytes(), &response)
require.NoError(t, err)
assert.True(t, response.Success)
}
// TestWebhookHandler_DeleteWebhook_NotFound tests webhook deletion with non-existent webhook
func TestWebhookHandler_DeleteWebhook_NotFound(t *testing.T) {
handler, db, router, cleanup := setupTestWebhookHandler(t)
defer cleanup()
// Create test user
userID := uuid.New()
user := createTestUserForWebhook(userID, "testuser")
err := db.Create(user).Error
require.NoError(t, err)
router.DELETE("/webhooks/:id", handler.DeleteWebhook())
nonExistentID := uuid.New()
req := httptest.NewRequest(http.MethodDelete, fmt.Sprintf("/webhooks/%s", nonExistentID.String()), nil)
req.Header.Set("X-User-ID", userID.String())
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusNotFound, w.Code)
}
// TestWebhookHandler_DeleteWebhook_InvalidID tests webhook deletion with invalid ID format
func TestWebhookHandler_DeleteWebhook_InvalidID(t *testing.T) {
handler, _, router, cleanup := setupTestWebhookHandler(t)
defer cleanup()
router.DELETE("/webhooks/:id", handler.DeleteWebhook())
req := httptest.NewRequest(http.MethodDelete, "/webhooks/invalid-id", nil)
req.Header.Set("X-User-ID", uuid.New().String())
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusBadRequest, w.Code)
}
// TestWebhookHandler_GetWebhookStats_Success tests successful webhook stats retrieval
func TestWebhookHandler_GetWebhookStats_Success(t *testing.T) {
handler, db, router, cleanup := setupTestWebhookHandler(t)
defer cleanup()
// Create test user
userID := uuid.New()
user := createTestUserForWebhook(userID, "testuser")
err := db.Create(user).Error
require.NoError(t, err)
router.GET("/webhooks/stats", handler.GetWebhookStats())
req := httptest.NewRequest(http.MethodGet, "/webhooks/stats", 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 APIResponse
err = json.Unmarshal(w.Body.Bytes(), &response)
require.NoError(t, err)
assert.True(t, response.Success)
}
// TestWebhookHandler_TestWebhook_Success tests successful webhook test
func TestWebhookHandler_TestWebhook_Success(t *testing.T) {
handler, db, router, cleanup := setupTestWebhookHandler(t)
defer cleanup()
// Create test user
userID := uuid.New()
user := createTestUserForWebhook(userID, "testuser")
err := db.Create(user).Error
require.NoError(t, err)
// Create test webhook
webhookID := uuid.New()
webhook := &models.Webhook{
ID: webhookID,
UserID: userID,
URL: "https://example.com/webhook",
Events: []string{"track.created"},
Active: true,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err = db.Create(webhook).Error
require.NoError(t, err)
router.POST("/webhooks/:id/test", handler.TestWebhook())
req := httptest.NewRequest(http.MethodPost, fmt.Sprintf("/webhooks/%s/test", webhookID.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 APIResponse
err = json.Unmarshal(w.Body.Bytes(), &response)
require.NoError(t, err)
assert.True(t, response.Success)
}
// TestWebhookHandler_TestWebhook_NotFound tests webhook test with non-existent webhook
func TestWebhookHandler_TestWebhook_NotFound(t *testing.T) {
handler, db, router, cleanup := setupTestWebhookHandler(t)
defer cleanup()
// Create test user
userID := uuid.New()
user := createTestUserForWebhook(userID, "testuser")
err := db.Create(user).Error
require.NoError(t, err)
router.POST("/webhooks/:id/test", handler.TestWebhook())
nonExistentID := uuid.New()
req := httptest.NewRequest(http.MethodPost, fmt.Sprintf("/webhooks/%s/test", nonExistentID.String()), nil)
req.Header.Set("X-User-ID", userID.String())
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusNotFound, w.Code)
}
// TestWebhookHandler_RegenerateAPIKey_Success tests successful API key regeneration
func TestWebhookHandler_RegenerateAPIKey_Success(t *testing.T) {
handler, db, router, cleanup := setupTestWebhookHandler(t)
defer cleanup()
// Create test user
userID := uuid.New()
user := createTestUserForWebhook(userID, "testuser")
err := db.Create(user).Error
require.NoError(t, err)
// Create test webhook
webhookID := uuid.New()
webhook := &models.Webhook{
ID: webhookID,
UserID: userID,
URL: "https://example.com/webhook",
Events: []string{"track.created"},
Active: true,
APIKey: "old_api_key",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err = db.Create(webhook).Error
require.NoError(t, err)
router.POST("/webhooks/:id/regenerate-key", handler.RegenerateAPIKey())
req := httptest.NewRequest(http.MethodPost, fmt.Sprintf("/webhooks/%s/regenerate-key", webhookID.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 APIResponse
err = json.Unmarshal(w.Body.Bytes(), &response)
require.NoError(t, err)
assert.True(t, response.Success)
// Verify that API key is present in response
dataBytes, _ := json.Marshal(response.Data)
var data map[string]interface{}
err = json.Unmarshal(dataBytes, &data)
require.NoError(t, err)
assert.Contains(t, data, "api_key")
assert.NotEmpty(t, data["api_key"])
}
// TestWebhookHandler_RegenerateAPIKey_NotFound tests API key regeneration with non-existent webhook
func TestWebhookHandler_RegenerateAPIKey_NotFound(t *testing.T) {
handler, db, router, cleanup := setupTestWebhookHandler(t)
defer cleanup()
// Create test user
userID := uuid.New()
user := createTestUserForWebhook(userID, "testuser")
err := db.Create(user).Error
require.NoError(t, err)
router.POST("/webhooks/:id/regenerate-key", handler.RegenerateAPIKey())
nonExistentID := uuid.New()
req := httptest.NewRequest(http.MethodPost, fmt.Sprintf("/webhooks/%s/regenerate-key", nonExistentID.String()), nil)
req.Header.Set("X-User-ID", userID.String())
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusNotFound, w.Code)
}
// TestWebhookHandler_RegenerateAPIKey_InvalidID tests API key regeneration with invalid ID format
func TestWebhookHandler_RegenerateAPIKey_InvalidID(t *testing.T) {
handler, _, router, cleanup := setupTestWebhookHandler(t)
defer cleanup()
router.POST("/webhooks/:id/regenerate-key", handler.RegenerateAPIKey())
req := httptest.NewRequest(http.MethodPost, "/webhooks/invalid-id/regenerate-key", nil)
req.Header.Set("X-User-ID", uuid.New().String())
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusBadRequest, w.Code)
}