//go:build integration // +build integration package integration import ( "context" "fmt" "net/http/httptest" "os" "testing" "time" "veza-backend-api/internal/api" "veza-backend-api/internal/config" "veza-backend-api/internal/database" "veza-backend-api/internal/metrics" "veza-backend-api/internal/models" "veza-backend-api/internal/services" "github.com/coder/websocket" "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" ) const testJWTSecret = "test-secret-key-minimum-32-characters-long" func setupChatTestRouter(t *testing.T) (*httptest.Server, *gorm.DB, func()) { t.Helper() os.Setenv("ENABLE_CLAMAV", "false") os.Setenv("CLAMAV_REQUIRED", "false") gin.SetMode(gin.TestMode) router := gin.New() logger := zaptest.NewLogger(t) mockGormDB, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{}) require.NoError(t, err) require.NoError(t, mockGormDB.AutoMigrate( &models.User{}, &models.Role{}, &models.Permission{}, &models.ChatMessage{}, &models.ReadReceipt{}, &models.DeliveredStatus{}, &models.MessageReaction{}, )) // Create room_members table for permissions mockGormDB.Exec(`CREATE TABLE IF NOT EXISTS room_members ( user_id TEXT NOT NULL, room_id TEXT NOT NULL, role TEXT DEFAULT 'member', is_muted INTEGER DEFAULT 0 )`) // Create rooms table mockGormDB.Exec(`CREATE TABLE IF NOT EXISTS rooms ( id TEXT PRIMARY KEY, name TEXT, type TEXT DEFAULT 'public' )`) mockDB := &database.Database{GormDB: mockGormDB} metrics.InitPrometheus() cfg := &config.Config{ Env: "test", ChatJWTSecret: testJWTSecret, HandlerTimeout: 30 * time.Second, LogLevel: "DEBUG", } apiRouter := api.NewAPIRouter(mockDB, cfg) err = apiRouter.Setup(router) require.NoError(t, err) ts := httptest.NewServer(router) cleanup := func() { ts.Close() } // Inject logger _ = logger return ts, mockGormDB, cleanup } func TestChatWS_AuthValid(t *testing.T) { ts, _, cleanup := setupChatTestRouter(t) defer cleanup() chatService := services.NewChatService(testJWTSecret, nil) userID := uuid.New() tokenResp, err := chatService.GenerateToken(userID, "testuser") require.NoError(t, err) wsURL := fmt.Sprintf("%s/api/v1/ws?token=%s", ts.URL, tokenResp.Token) wsURL = "ws" + wsURL[4:] ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() conn, _, err := websocket.Dial(ctx, wsURL, nil) require.NoError(t, err) defer conn.Close(websocket.StatusNormalClosure, "") // Should receive ActionConfirmed connected message _, msg, err := conn.Read(ctx) require.NoError(t, err) assert.Contains(t, string(msg), "connected") } func TestChatWS_AuthMissingToken(t *testing.T) { ts, _, cleanup := setupChatTestRouter(t) defer cleanup() wsURL := fmt.Sprintf("%s/api/v1/ws", ts.URL) wsURL = "ws" + wsURL[4:] ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() _, _, err := websocket.Dial(ctx, wsURL, nil) assert.Error(t, err) } func TestChatWS_AuthInvalidToken(t *testing.T) { ts, _, cleanup := setupChatTestRouter(t) defer cleanup() wsURL := fmt.Sprintf("%s/api/v1/ws?token=invalid-token", ts.URL) wsURL = "ws" + wsURL[4:] ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() _, _, err := websocket.Dial(ctx, wsURL, nil) assert.Error(t, err) } func TestChatWS_PingPong(t *testing.T) { ts, _, cleanup := setupChatTestRouter(t) defer cleanup() chatService := services.NewChatService(testJWTSecret, nil) userID := uuid.New() tokenResp, err := chatService.GenerateToken(userID, "testuser") require.NoError(t, err) wsURL := fmt.Sprintf("%s/api/v1/ws?token=%s", ts.URL, tokenResp.Token) wsURL = "ws" + wsURL[4:] ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() conn, _, err := websocket.Dial(ctx, wsURL, nil) require.NoError(t, err) defer conn.Close(websocket.StatusNormalClosure, "") // Read ActionConfirmed _, _, err = conn.Read(ctx) require.NoError(t, err) // Send Ping err = conn.Write(ctx, websocket.MessageText, []byte(`{"type":"Ping"}`)) require.NoError(t, err) // Expect Pong _, msg, err := conn.Read(ctx) require.NoError(t, err) assert.Contains(t, string(msg), "Pong") }