package api import ( "bytes" "net/http" "net/http/httptest" "os" "testing" "time" "github.com/gin-gonic/gin" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap" "gorm.io/driver/sqlite" "gorm.io/gorm" "veza-backend-api/internal/config" "veza-backend-api/internal/database" "veza-backend-api/internal/metrics" ) func setupWebhookTestRouter(t *testing.T, webhookSecret string) *gin.Engine { t.Helper() os.Setenv("ENABLE_CLAMAV", "false") os.Setenv("CLAMAV_REQUIRED", "false") gin.SetMode(gin.TestMode) router := gin.New() gormDB, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{}) require.NoError(t, err) sqlDB, err := gormDB.DB() require.NoError(t, err) vezaDB := &database.Database{ DB: sqlDB, GormDB: gormDB, Logger: zap.NewNop(), } cfg := &config.Config{ HyperswitchWebhookSecret: webhookSecret, JWTSecret: "test-jwt-secret-key-minimum-32-characters-long", JWTIssuer: "veza-api", JWTAudience: "veza-app", Logger: zap.NewNop(), RedisClient: nil, ErrorMetrics: metrics.NewErrorMetrics(), UploadDir: "uploads/test", Env: "development", Database: vezaDB, CORSOrigins: []string{"*"}, HandlerTimeout: 30 * time.Second, RateLimitLimit: 100, RateLimitWindow: 60, AuthRateLimitLoginAttempts: 10, AuthRateLimitLoginWindow: 15, } require.NoError(t, cfg.InitServicesForTest()) require.NoError(t, cfg.InitMiddlewaresForTest()) apiRouter := NewAPIRouter(vezaDB, cfg) require.NoError(t, apiRouter.Setup(router)) return router } // TestHyperswitchWebhook_EmptySecret_Returns500 verifies that when HyperswitchWebhookSecret // is empty, the webhook handler returns 500 (VEZA-SEC-005). func TestHyperswitchWebhook_EmptySecret_Returns500(t *testing.T) { router := setupWebhookTestRouter(t, "") req := httptest.NewRequest(http.MethodPost, "/api/v1/webhooks/hyperswitch", bytes.NewReader([]byte(`{"test": true}`))) req.Header.Set("Content-Type", "application/json") w := httptest.NewRecorder() router.ServeHTTP(w, req) assert.Equal(t, http.StatusInternalServerError, w.Code, "empty webhook secret must return 500") } // TestHyperswitchWebhook_ValidSecret_InvalidSignature_Returns401 verifies that with a configured secret, // invalid signature returns 401 (smoke test that verification path is exercised). func TestHyperswitchWebhook_ValidSecret_InvalidSignature_Returns401(t *testing.T) { router := setupWebhookTestRouter(t, "test-secret-at-least-32-chars-long") req := httptest.NewRequest(http.MethodPost, "/api/v1/webhooks/hyperswitch", bytes.NewReader([]byte(`{"test": true}`))) req.Header.Set("Content-Type", "application/json") w := httptest.NewRecorder() router.ServeHTTP(w, req) assert.Equal(t, http.StatusUnauthorized, w.Code, "invalid signature must return 401") }