//go:build integration // +build integration package integration import ( "context" "encoding/json" "fmt" "testing" "time" "veza-backend-api/internal/services" "github.com/coder/websocket" "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestChatWS_SendMessage_Flow(t *testing.T) { ts, db, cleanup := setupChatTestRouter(t) defer cleanup() roomID := uuid.New() user1ID := uuid.New() user2ID := uuid.New() // Setup room membership db.Exec("INSERT INTO rooms (id, name, type) VALUES (?, 'test', 'public')", roomID) db.Exec("INSERT INTO room_members (user_id, room_id, role, is_muted) VALUES (?, ?, 'member', 0)", user1ID, roomID) db.Exec("INSERT INTO room_members (user_id, room_id, role, is_muted) VALUES (?, ?, 'member', 0)", user2ID, roomID) chatService := services.NewChatService(testJWTSecret, nil) token1, err := chatService.GenerateToken(user1ID, "user1") require.NoError(t, err) token2, err := chatService.GenerateToken(user2ID, "user2") require.NoError(t, err) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() // Connect user1 wsURL1 := fmt.Sprintf("ws%s/api/v1/ws?token=%s", ts.URL[4:], token1.Token) conn1, _, err := websocket.Dial(ctx, wsURL1, nil) require.NoError(t, err) defer conn1.Close(websocket.StatusNormalClosure, "") // Connect user2 wsURL2 := fmt.Sprintf("ws%s/api/v1/ws?token=%s", ts.URL[4:], token2.Token) conn2, _, err := websocket.Dial(ctx, wsURL2, nil) require.NoError(t, err) defer conn2.Close(websocket.StatusNormalClosure, "") // Read ActionConfirmed for both _, _, _ = conn1.Read(ctx) _, _, _ = conn2.Read(ctx) // Both join the room joinMsg := fmt.Sprintf(`{"type":"JoinConversation","conversation_id":"%s"}`, roomID) err = conn1.Write(ctx, websocket.MessageText, []byte(joinMsg)) require.NoError(t, err) err = conn2.Write(ctx, websocket.MessageText, []byte(joinMsg)) require.NoError(t, err) // Read join confirmations _, _, _ = conn1.Read(ctx) _, _, _ = conn2.Read(ctx) // User1 sends a message sendMsg := fmt.Sprintf(`{"type":"SendMessage","conversation_id":"%s","content":"Hello from user1!"}`, roomID) err = conn1.Write(ctx, websocket.MessageText, []byte(sendMsg)) require.NoError(t, err) // User1 should get ActionConfirmed _, msg1, err := conn1.Read(ctx) require.NoError(t, err) var confirmed map[string]interface{} json.Unmarshal(msg1, &confirmed) assert.Equal(t, "ActionConfirmed", confirmed["type"]) // Both should get NewMessage broadcast _, msg1Broadcast, err := conn1.Read(ctx) require.NoError(t, err) var newMsg1 map[string]interface{} json.Unmarshal(msg1Broadcast, &newMsg1) assert.Equal(t, "NewMessage", newMsg1["type"]) assert.Equal(t, "Hello from user1!", newMsg1["content"]) _, msg2Broadcast, err := conn2.Read(ctx) require.NoError(t, err) var newMsg2 map[string]interface{} json.Unmarshal(msg2Broadcast, &newMsg2) assert.Equal(t, "NewMessage", newMsg2["type"]) assert.Equal(t, "Hello from user1!", newMsg2["content"]) } func TestChatWS_TypingIndicator(t *testing.T) { ts, db, cleanup := setupChatTestRouter(t) defer cleanup() roomID := uuid.New() user1ID := uuid.New() user2ID := uuid.New() db.Exec("INSERT INTO rooms (id, name, type) VALUES (?, 'test', 'public')", roomID) db.Exec("INSERT INTO room_members (user_id, room_id, role, is_muted) VALUES (?, ?, 'member', 0)", user1ID, roomID) db.Exec("INSERT INTO room_members (user_id, room_id, role, is_muted) VALUES (?, ?, 'member', 0)", user2ID, roomID) chatService := services.NewChatService(testJWTSecret, nil) token1, _ := chatService.GenerateToken(user1ID, "user1") token2, _ := chatService.GenerateToken(user2ID, "user2") ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() conn1, _, _ := websocket.Dial(ctx, fmt.Sprintf("ws%s/api/v1/ws?token=%s", ts.URL[4:], token1.Token), nil) conn2, _, _ := websocket.Dial(ctx, fmt.Sprintf("ws%s/api/v1/ws?token=%s", ts.URL[4:], token2.Token), nil) defer conn1.Close(websocket.StatusNormalClosure, "") defer conn2.Close(websocket.StatusNormalClosure, "") _, _, _ = conn1.Read(ctx) _, _, _ = conn2.Read(ctx) joinMsg := fmt.Sprintf(`{"type":"JoinConversation","conversation_id":"%s"}`, roomID) conn1.Write(ctx, websocket.MessageText, []byte(joinMsg)) conn2.Write(ctx, websocket.MessageText, []byte(joinMsg)) _, _, _ = conn1.Read(ctx) _, _, _ = conn2.Read(ctx) // User1 types typingMsg := fmt.Sprintf(`{"type":"Typing","conversation_id":"%s","is_typing":true}`, roomID) conn1.Write(ctx, websocket.MessageText, []byte(typingMsg)) // User1 gets ActionConfirmed _, _, _ = conn1.Read(ctx) // User2 should see typing indicator _, typing, err := conn2.Read(ctx) require.NoError(t, err) var typingResp map[string]interface{} json.Unmarshal(typing, &typingResp) assert.Equal(t, "UserTyping", typingResp["type"]) assert.Equal(t, true, typingResp["is_typing"]) }