- 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
487 lines
14 KiB
Go
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)
|
|
}
|