package chat import ( "context" "encoding/json" "testing" "time" "veza-backend-api/internal/models" "veza-backend-api/internal/repositories" "veza-backend-api/internal/services" "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" ) func setupTestHandler(t *testing.T) (*MessageHandler, *Hub, *gorm.DB) { t.Helper() logger := zaptest.NewLogger(t) db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{}) require.NoError(t, err) require.NoError(t, db.AutoMigrate( &models.ChatMessage{}, &models.ReadReceipt{}, &models.DeliveredStatus{}, &models.MessageReaction{}, )) hub := NewHub(logger) go hub.Run() time.Sleep(10 * time.Millisecond) msgRepo := repositories.NewChatMessageRepository(db) readRepo := repositories.NewReadReceiptRepository(db) deliveredRepo := repositories.NewDeliveredStatusRepository(db) reactionRepo := repositories.NewReactionRepository(db) pubsub := services.NewChatPubSubService(nil, logger) permissions := &PermissionService{db: db, logger: logger} rateLimiter := NewRateLimiter() handler := NewMessageHandler( hub, msgRepo, readRepo, deliveredRepo, reactionRepo, pubsub, permissions, rateLimiter, logger, ) return handler, hub, db } func TestHandleSendMessage_Success(t *testing.T) { handler, hub, db := setupTestHandler(t) ctx := context.Background() userID := uuid.New() roomID := uuid.New() db.Exec("CREATE TABLE IF NOT EXISTS room_members (user_id TEXT, room_id TEXT, role TEXT DEFAULT 'member', is_muted INTEGER DEFAULT 0)") db.Exec("INSERT INTO room_members (user_id, room_id, role, is_muted) VALUES (?, ?, 'member', 0)", userID, roomID) client := newTestClient(hub, userID) hub.Register(client) hub.JoinRoom(client, roomID) time.Sleep(20 * time.Millisecond) msg := &IncomingMessage{ Type: TypeSendMessage, ConversationID: &roomID, Content: "Hello World", } handler.HandleSendMessage(ctx, client, msg) time.Sleep(20 * time.Millisecond) // Check we got ActionConfirmed and NewMessage broadcast assert.GreaterOrEqual(t, len(client.send), 1) // Verify message was stored in DB var count int64 db.Model(&models.ChatMessage{}).Where("room_id = ?", roomID).Count(&count) assert.Equal(t, int64(1), count) } func TestHandleSendMessage_MissingConversationID(t *testing.T) { handler, hub, _ := setupTestHandler(t) ctx := context.Background() userID := uuid.New() client := newTestClient(hub, userID) hub.Register(client) time.Sleep(20 * time.Millisecond) msg := &IncomingMessage{ Type: TypeSendMessage, Content: "Hello", } handler.HandleSendMessage(ctx, client, msg) time.Sleep(20 * time.Millisecond) assert.Len(t, client.send, 1) resp := <-client.send var parsed map[string]interface{} json.Unmarshal(resp, &parsed) assert.Equal(t, TypeError, parsed["type"]) } func TestHandleEditMessage_OwnershipCheck(t *testing.T) { handler, hub, db := setupTestHandler(t) ctx := context.Background() senderID := uuid.New() otherID := uuid.New() roomID := uuid.New() msgID := uuid.New() existingMsg := &models.ChatMessage{ ID: msgID, ConversationID: roomID, SenderID: senderID, Content: "original", MessageType: "text", Status: "sent", CreatedAt: time.Now(), UpdatedAt: time.Now(), } db.Create(existingMsg) otherClient := newTestClient(hub, otherID) hub.Register(otherClient) time.Sleep(20 * time.Millisecond) msg := &IncomingMessage{ Type: TypeEditMessage, MessageID: &msgID, ConversationID: &roomID, NewContent: "modified", } handler.HandleEditMessage(ctx, otherClient, msg) time.Sleep(20 * time.Millisecond) assert.Len(t, otherClient.send, 1) resp := <-otherClient.send var parsed map[string]interface{} json.Unmarshal(resp, &parsed) assert.Equal(t, TypeError, parsed["type"]) assert.Contains(t, parsed["message"], "own messages") } func TestHandleDeleteMessage_SoftDelete(t *testing.T) { handler, hub, db := setupTestHandler(t) ctx := context.Background() userID := uuid.New() roomID := uuid.New() msgID := uuid.New() db.Exec("CREATE TABLE IF NOT EXISTS room_members (user_id TEXT, room_id TEXT, role TEXT DEFAULT 'member', is_muted INTEGER DEFAULT 0)") db.Exec("INSERT INTO room_members (user_id, room_id, role, is_muted) VALUES (?, ?, 'member', 0)", userID, roomID) existingMsg := &models.ChatMessage{ ID: msgID, ConversationID: roomID, SenderID: userID, Content: "to delete", MessageType: "text", Status: "sent", CreatedAt: time.Now(), UpdatedAt: time.Now(), } db.Create(existingMsg) client := newTestClient(hub, userID) hub.Register(client) hub.JoinRoom(client, roomID) time.Sleep(20 * time.Millisecond) msg := &IncomingMessage{ Type: TypeDeleteMessage, MessageID: &msgID, ConversationID: &roomID, } handler.HandleDeleteMessage(ctx, client, msg) time.Sleep(20 * time.Millisecond) var deletedMsg models.ChatMessage db.Where("id = ?", msgID).First(&deletedMsg) assert.True(t, deletedMsg.IsDeleted) }