package graph import ( "context" "fmt" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "tailly_back_v2/proto" ) const ( grpcAddress = "localhost:50052" userIDKey = "userID" ) func setupIntegrationTest(t *testing.T) *Resolver { // Создаем соединение с gRPC сервером conn, err := grpc.Dial(grpcAddress, grpc.WithTransportCredentials(insecure.NewCredentials())) require.NoError(t, err, "failed to connect to gRPC server") return &Resolver{ MessageClient: proto.NewMessageServiceClient(conn), } } func TestIntegration_CreateAndSendMessages(t *testing.T) { r := setupIntegrationTest(t) ctx := context.Background() // Тест создания чата t.Run("CreateChat", func(t *testing.T) { resolver := &mutationResolver{r} chat, err := resolver.CreateChat(ctx, 1, 2) require.NoError(t, err) assert.NotZero(t, chat.ID) assert.Equal(t, 1, chat.User1ID) assert.Equal(t, 2, chat.User2ID) }) // Тест отправки сообщений t.Run("SendMessages", func(t *testing.T) { resolver := &mutationResolver{r} // Сначала создаем чат chat, err := resolver.CreateChat(ctx, 1, 2) require.NoError(t, err) // Отправляем 5 сообщений for i := 0; i < 5; i++ { msg, err := resolver.SendMessage( context.WithValue(ctx, userIDKey, 1), // Добавляем senderID в контекст int(chat.ID), "test message "+string(rune('A'+i)), ) require.NoError(t, err) assert.NotZero(t, msg.ID) assert.Equal(t, "test message "+string(rune('A'+i)), msg.Content) } }) } func TestIntegration_MessageStream(t *testing.T) { r := setupIntegrationTest(t) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() // Используем существующих пользователей const user1ID = 5 const user2ID = 12 // Создаем чат mutation := &mutationResolver{r} chat, err := mutation.CreateChat(ctx, user1ID, user2ID) require.NoError(t, err, "Failed to create chat") // Канал для синхронизации завершения теста testDone := make(chan struct{}) defer close(testDone) // Канал для получения ошибок из горутин errChan := make(chan error, 1) // Запускаем подписку на сообщения subscription := &subscriptionResolver{r} msgChan, err := subscription.MessageStream(ctx, user2ID) require.NoError(t, err, "Failed to create message stream") // Отправляем 5 сообщений go func() { defer func() { if r := recover(); r != nil { t.Logf("Recovered in sender goroutine: %v", r) } }() for i := 0; i < 5; i++ { select { case <-testDone: return default: _, err := mutation.SendMessage( context.WithValue(ctx, userIDKey, user1ID), int(chat.ID), fmt.Sprintf("stream message %d", i+1), ) if err != nil { select { case errChan <- fmt.Errorf("failed to send message: %w", err): default: } return } time.Sleep(500 * time.Millisecond) // Увеличиваем задержку } } }() // Получаем сообщения из стрима received := 0 var lastErr error for { select { case err := <-errChan: lastErr = err break case msg, ok := <-msgChan: if !ok { break } if msg == nil { continue // Пропускаем nil-сообщения (heartbeats) } t.Logf("Received message %d: %s", received+1, msg.Content) received++ if received >= 5 { return // Успешно получили все сообщения } case <-ctx.Done(): lastErr = ctx.Err() break } // Выходим из цикла если была ошибка или завершение if lastErr != nil { break } } if received < 5 { if lastErr != nil { require.NoError(t, lastErr, "Didn't receive all messages") } else { t.Errorf("Expected 5 messages, received %d", received) } } } func TestIntegration_GetChatMessages(t *testing.T) { r := setupIntegrationTest(t) ctx := context.Background() // Используем существующих пользователей const user1ID = 5 const user2ID = 12 // Создаем чат mutation := &mutationResolver{r} chat, err := mutation.CreateChat(ctx, user1ID, user2ID) require.NoError(t, err, "Failed to create chat") // Отправляем ровно 5 сообщений expectedContents := make([]string, 5) for i := 0; i < 5; i++ { content := fmt.Sprintf("test message %d", i+1) _, err := mutation.SendMessage( context.WithValue(ctx, userIDKey, user1ID), int(chat.ID), content, ) require.NoError(t, err, "Failed to send message") expectedContents[i] = content } // Даем время на обработку time.Sleep(1 * time.Second) // Проверяем получение сообщений query := &queryResolver{r} messages, err := query.GetChatMessages(ctx, int(chat.ID), 10, 0) require.NoError(t, err, "Failed to get chat messages") // Проверяем что получили хотя бы 5 сообщений assert.GreaterOrEqual(t, len(messages), 5, "Expected at least 5 messages") // Проверяем последние 5 сообщений for i := 0; i < 5; i++ { assert.Equal(t, expectedContents[4-i], messages[i].Content, "Message content mismatch") } } func TestIntegration_GetUserChats(t *testing.T) { r := setupIntegrationTest(t) ctx := context.Background() // Создаем несколько чатов mutation := &mutationResolver{r} _, err := mutation.CreateChat(ctx, 1, 2) require.NoError(t, err) _, err = mutation.CreateChat(ctx, 1, 3) require.NoError(t, err) // Проверяем получение чатов пользователя query := &queryResolver{r} chats, err := query.GetUserChats(ctx, 1) require.NoError(t, err) assert.GreaterOrEqual(t, len(chats), 2) } func TestIntegration_UpdateMessageStatus(t *testing.T) { r := setupIntegrationTest(t) ctx := context.Background() // Используем существующих пользователей const user1ID = 5 const user2ID = 12 // Создаем чат mutation := &mutationResolver{r} chat, err := mutation.CreateChat(ctx, user1ID, user2ID) require.NoError(t, err, "Failed to create chat") // Отправляем тестовое сообщение msg, err := mutation.SendMessage( context.WithValue(ctx, userIDKey, user1ID), int(chat.ID), "status test", ) require.NoError(t, err, "Failed to send message") // Даем время на обработку time.Sleep(500 * time.Millisecond) // Определяем поддерживаемые статусы из вашего сервера // (должны соответствовать тому, что действительно принимает сервер) validStatuses := []struct { name string status string }{ {"SENT", "SENT"}, {"DELIVERED", "DELIVERED"}, {"READ", "READ"}, } for _, ts := range validStatuses { t.Run(fmt.Sprintf("Update to %s", ts.name), func(t *testing.T) { // Преобразуем строковый статус в MessageStatus var msgStatus MessageStatus switch ts.status { case "SENT": msgStatus = MessageStatusSent case "DELIVERED": msgStatus = MessageStatusDelivered case "READ": msgStatus = MessageStatusRead default: t.Fatalf("Unsupported status: %s", ts.status) } updatedMsg, err := mutation.UpdateMessageStatus(ctx, msg.ID, msgStatus) if err != nil { t.Logf("If this fails, check your server's message status constraints") } require.NoError(t, err, "Failed to update message status") assert.Equal(t, ts.status, updatedMsg.Status, "Status mismatch") }) } }