This commit is contained in:
Mikhail Kilin
2026-01-31 23:02:53 +03:00
parent af3c36b9a1
commit c5896b7f14
17 changed files with 1899 additions and 567 deletions

418
tests/e2e_user_journey.rs Normal file
View File

@@ -0,0 +1,418 @@
// E2E User Journey tests — многошаговые интеграционные тесты
mod helpers;
use helpers::fake_tdclient::{FakeTdClient, TdUpdate};
use helpers::test_data::{TestChatBuilder, TestMessageBuilder};
use tele_tui::tdlib::NetworkState;
use tele_tui::types::{ChatId, MessageId};
/// Тест 1: App Launch → Auth → Chat List
/// Симулирует полный путь пользователя от запуска до загрузки чатов
#[tokio::test]
async fn test_user_journey_app_launch_to_chat_list() {
// 1. Создаем fake client (симуляция авторизации пропущена, клиент уже авторизован)
let client = FakeTdClient::new();
// 2. Проверяем начальное состояние - нет чатов
assert_eq!(client.get_chats().len(), 0);
assert_eq!(client.get_network_state(), NetworkState::Ready);
// 3. Создаем чаты
let chat1 = TestChatBuilder::new("Mom", 101).build();
let chat2 = TestChatBuilder::new("Work Group", 102).build();
let chat3 = TestChatBuilder::new("Boss", 103).build();
let client = client
.with_chat(chat1)
.with_chat(chat2)
.with_chat(chat3);
// 4. Симулируем загрузку чатов через load_chats
let loaded_chats = client.load_chats(50).await.unwrap();
// 5. Проверяем что чаты загружены
assert_eq!(loaded_chats.len(), 3);
assert_eq!(loaded_chats[0].title, "Mom");
assert_eq!(loaded_chats[1].title, "Work Group");
assert_eq!(loaded_chats[2].title, "Boss");
// 6. Проверяем что нет выбранного чата
assert_eq!(client.get_current_chat_id(), None);
}
/// Тест 2: Open Chat → Load History → Send Message
/// Симулирует открытие чата, загрузку истории и отправку сообщения
#[tokio::test]
async fn test_user_journey_open_chat_send_message() {
// 1. Подготовка: создаем клиент с чатом
let chat = TestChatBuilder::new("Mom", 123).build();
let client = FakeTdClient::new().with_chat(chat);
// 2. Создаем несколько сообщений в истории
let msg1 = TestMessageBuilder::new("Hi, how are you?", 1)
.sender("Mom")
.build();
let msg2 = TestMessageBuilder::new("I'm good, thanks!", 2)
.outgoing()
.build();
let client = client
.with_message(123, msg1)
.with_message(123, msg2);
// 3. Открываем чат
client.open_chat(ChatId::new(123)).await.unwrap();
// 4. Проверяем что чат открыт
assert_eq!(client.get_current_chat_id(), Some(123));
// 5. Загружаем историю сообщений
let history = client.get_chat_history(ChatId::new(123), 50).await.unwrap();
// 6. Проверяем что история загружена
assert_eq!(history.len(), 2);
assert_eq!(history[0].text(), "Hi, how are you?");
assert_eq!(history[1].text(), "I'm good, thanks!");
// 7. Отправляем новое сообщение
let _new_msg = client.send_message(
ChatId::new(123),
"What's for dinner?".to_string(),
None,
None
).await.unwrap();
// 8. Проверяем что сообщение отправлено
assert_eq!(client.get_sent_messages().len(), 1);
assert_eq!(client.get_sent_messages()[0].text, "What's for dinner?");
assert_eq!(client.get_sent_messages()[0].chat_id, 123);
// 9. Проверяем что сообщение добавилось в историю
let updated_history = client.get_chat_history(ChatId::new(123), 50).await.unwrap();
assert_eq!(updated_history.len(), 3);
assert_eq!(updated_history[2].text(), "What's for dinner?");
}
/// Тест 3: Receive Incoming Message While Chat Open
/// Симулирует получение входящего сообщения в открытом чате
#[tokio::test]
async fn test_user_journey_receive_incoming_message() {
// 1. Подготовка: создаем клиент с открытым чатом
let chat = TestChatBuilder::new("Friend", 456).build();
let client = FakeTdClient::new().with_chat(chat);
// 2. Открываем чат
client.open_chat(ChatId::new(456)).await.unwrap();
assert_eq!(client.get_current_chat_id(), Some(456));
// 3. Создаем update channel для получения событий
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();
client.set_update_channel(tx);
// 4. Проверяем начальное состояние - нет сообщений
let initial_history = client.get_chat_history(ChatId::new(456), 50).await.unwrap();
assert_eq!(initial_history.len(), 0);
// 5. Симулируем входящее сообщение от собеседника
client.simulate_incoming_message(ChatId::new(456), "Hey! Are you there?".to_string(), "Friend");
// 6. Получаем update из канала
let update = rx.try_recv();
assert!(update.is_ok(), "Должен быть получен update о новом сообщении");
if let Ok(TdUpdate::NewMessage { chat_id, message }) = update {
assert_eq!(chat_id.as_i64(), 456);
assert_eq!(message.text(), "Hey! Are you there?");
assert_eq!(message.sender_name(), "Friend");
assert!(!message.is_outgoing());
} else {
panic!("Неверный тип update");
}
// 7. Проверяем что сообщение появилось в истории
let updated_history = client.get_chat_history(ChatId::new(456), 50).await.unwrap();
assert_eq!(updated_history.len(), 1);
assert_eq!(updated_history[0].text(), "Hey! Are you there?");
}
/// Тест 4: Multi-step conversation flow
/// Симулирует полноценную беседу с несколькими сообщениями туда-обратно
#[tokio::test]
async fn test_user_journey_multi_step_conversation() {
// 1. Подготовка
let chat = TestChatBuilder::new("Alice", 789).build();
let client = FakeTdClient::new().with_chat(chat);
// 2. Открываем чат
client.open_chat(ChatId::new(789)).await.unwrap();
// 3. Setup update channel
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();
client.set_update_channel(tx);
// 4. Входящее сообщение от Alice
client.simulate_incoming_message(ChatId::new(789), "How's the project going?".to_string(), "Alice");
// Проверяем update
let update = rx.try_recv().ok();
assert!(matches!(update, Some(TdUpdate::NewMessage { .. })));
// 5. Отвечаем
client.send_message(
ChatId::new(789),
"Almost done! Just need to finish tests.".to_string(),
None,
None
).await.unwrap();
// 6. Проверяем историю после первого обмена
let history1 = client.get_chat_history(ChatId::new(789), 50).await.unwrap();
assert_eq!(history1.len(), 2);
// 7. Еще одно входящее сообщение
client.simulate_incoming_message(ChatId::new(789), "Great! Let me know if you need help.".to_string(), "Alice");
// 8. Снова отвечаем
client.send_message(
ChatId::new(789),
"Will do, thanks!".to_string(),
None,
None
).await.unwrap();
// 9. Финальная проверка истории
let final_history = client.get_chat_history(ChatId::new(789), 50).await.unwrap();
assert_eq!(final_history.len(), 4);
// Проверяем порядок сообщений
assert_eq!(final_history[0].text(), "How's the project going?");
assert!(!final_history[0].is_outgoing());
assert_eq!(final_history[1].text(), "Almost done! Just need to finish tests.");
assert!(final_history[1].is_outgoing());
assert_eq!(final_history[2].text(), "Great! Let me know if you need help.");
assert!(!final_history[2].is_outgoing());
assert_eq!(final_history[3].text(), "Will do, thanks!");
assert!(final_history[3].is_outgoing());
}
/// Тест 5: Switch between chats
/// Симулирует переключение между разными чатами
#[tokio::test]
async fn test_user_journey_switch_chats() {
// 1. Создаем несколько чатов
let chat1 = TestChatBuilder::new("Chat 1", 111).build();
let chat2 = TestChatBuilder::new("Chat 2", 222).build();
let chat3 = TestChatBuilder::new("Chat 3", 333).build();
let client = FakeTdClient::new()
.with_chat(chat1)
.with_chat(chat2)
.with_chat(chat3);
// 2. Открываем первый чат
client.open_chat(ChatId::new(111)).await.unwrap();
assert_eq!(client.get_current_chat_id(), Some(111));
// 3. Отправляем сообщение в первом чате
client.send_message(
ChatId::new(111),
"Message in chat 1".to_string(),
None,
None
).await.unwrap();
// 4. Переключаемся на второй чат
client.open_chat(ChatId::new(222)).await.unwrap();
assert_eq!(client.get_current_chat_id(), Some(222));
// 5. Отправляем сообщение во втором чате
client.send_message(
ChatId::new(222),
"Message in chat 2".to_string(),
None,
None
).await.unwrap();
// 6. Переключаемся на третий чат
client.open_chat(ChatId::new(333)).await.unwrap();
assert_eq!(client.get_current_chat_id(), Some(333));
// 7. Проверяем что сообщения были отправлены в правильные чаты
assert_eq!(client.get_sent_messages().len(), 2);
assert_eq!(client.get_sent_messages()[0].chat_id, 111);
assert_eq!(client.get_sent_messages()[0].text, "Message in chat 1");
assert_eq!(client.get_sent_messages()[1].chat_id, 222);
assert_eq!(client.get_sent_messages()[1].text, "Message in chat 2");
// 8. Проверяем истории отдельных чатов
let hist1 = client.get_chat_history(ChatId::new(111), 50).await.unwrap();
let hist2 = client.get_chat_history(ChatId::new(222), 50).await.unwrap();
let hist3 = client.get_chat_history(ChatId::new(333), 50).await.unwrap();
assert_eq!(hist1.len(), 1);
assert_eq!(hist2.len(), 1);
assert_eq!(hist3.len(), 0);
}
/// Тест 6: Edit message in conversation flow
/// Симулирует редактирование сообщения в процессе беседы
#[tokio::test]
async fn test_user_journey_edit_during_conversation() {
// 1. Подготовка
let chat = TestChatBuilder::new("Bob", 555).build();
let client = FakeTdClient::new().with_chat(chat);
client.open_chat(ChatId::new(555)).await.unwrap();
// 2. Отправляем сообщение с опечаткой
let msg = client.send_message(
ChatId::new(555),
"I'll be there at 5pm tomorow".to_string(),
None,
None
).await.unwrap();
// 3. Проверяем что сообщение отправлено
let history = client.get_chat_history(ChatId::new(555), 50).await.unwrap();
assert_eq!(history.len(), 1);
assert_eq!(history[0].text(), "I'll be there at 5pm tomorow");
// 4. Исправляем опечатку
client.edit_message(
ChatId::new(555),
msg.id(),
"I'll be there at 5pm tomorrow".to_string()
).await.unwrap();
// 5. Проверяем что сообщение отредактировано
let edited_history = client.get_chat_history(ChatId::new(555), 50).await.unwrap();
assert_eq!(edited_history.len(), 1);
assert_eq!(edited_history[0].text(), "I'll be there at 5pm tomorrow");
assert!(edited_history[0].edit_date() > 0, "Должна быть установлена дата редактирования");
// 6. Проверяем историю редактирований
assert_eq!(client.get_edited_messages().len(), 1);
assert_eq!(client.get_edited_messages()[0].new_text, "I'll be there at 5pm tomorrow");
}
/// Тест 7: Reply to message in conversation
/// Симулирует ответ на конкретное сообщение
#[tokio::test]
async fn test_user_journey_reply_in_conversation() {
// 1. Подготовка
let chat = TestChatBuilder::new("Charlie", 666).build();
let client = FakeTdClient::new().with_chat(chat);
client.open_chat(ChatId::new(666)).await.unwrap();
// 2. Setup updates
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();
client.set_update_channel(tx);
// 3. Входящее сообщение с вопросом
client.simulate_incoming_message(ChatId::new(666), "Can you send me the report?".to_string(), "Charlie");
let update = rx.try_recv().ok();
assert!(matches!(update, Some(TdUpdate::NewMessage { .. })));
let history = client.get_chat_history(ChatId::new(666), 50).await.unwrap();
let question_msg_id = history[0].id();
// 4. Отправляем другое сообщение (не связанное)
client.send_message(
ChatId::new(666),
"Working on it now".to_string(),
None,
None
).await.unwrap();
// 5. Отвечаем на конкретный вопрос (reply)
let reply_info = Some(tele_tui::tdlib::ReplyInfo {
message_id: question_msg_id,
sender_name: "Charlie".to_string(),
text: "Can you send me the report?".to_string(),
});
client.send_message(
ChatId::new(666),
"Sure, sending now!".to_string(),
Some(question_msg_id),
reply_info
).await.unwrap();
// 6. Проверяем что reply сохранён
let final_history = client.get_chat_history(ChatId::new(666), 50).await.unwrap();
assert_eq!(final_history.len(), 3);
// Последнее сообщение должно быть reply
let reply_msg = &final_history[2];
assert_eq!(reply_msg.text(), "Sure, sending now!");
assert!(reply_msg.interactions.reply_to.is_some());
let reply_to = reply_msg.interactions.reply_to.as_ref().unwrap();
assert_eq!(reply_to.message_id, question_msg_id);
assert_eq!(reply_to.text, "Can you send me the report?");
}
/// Тест 8: Network state changes during conversation
/// Симулирует изменения состояния сети во время работы
#[tokio::test]
async fn test_user_journey_network_state_changes() {
// 1. Подготовка
let chat = TestChatBuilder::new("Network Test", 888).build();
let client = FakeTdClient::new().with_chat(chat);
// 2. Setup updates
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();
client.set_update_channel(tx);
// 3. Начальное состояние - Ready
assert_eq!(client.get_network_state(), NetworkState::Ready);
// 4. Открываем чат и отправляем сообщение
client.open_chat(ChatId::new(888)).await.unwrap();
client.send_message(
ChatId::new(888),
"Test message".to_string(),
None,
None
).await.unwrap();
// Очищаем канал от update NewMessage
let _ = rx.try_recv();
// 5. Симулируем потерю сети
client.simulate_network_change(NetworkState::WaitingForNetwork);
// Проверяем update
let update = rx.try_recv().ok();
assert!(matches!(update, Some(TdUpdate::ConnectionState { state: NetworkState::WaitingForNetwork })),
"Expected ConnectionState update, got: {:?}", update);
// 6. Проверяем что состояние изменилось
assert_eq!(client.get_network_state(), NetworkState::WaitingForNetwork);
// 7. Симулируем восстановление соединения
client.simulate_network_change(NetworkState::Connecting);
assert_eq!(client.get_network_state(), NetworkState::Connecting);
client.simulate_network_change(NetworkState::Ready);
assert_eq!(client.get_network_state(), NetworkState::Ready);
// 8. Отправляем сообщение после восстановления
client.send_message(
ChatId::new(888),
"Connection restored!".to_string(),
None,
None
).await.unwrap();
// 9. Проверяем что оба сообщения в истории
let history = client.get_chat_history(ChatId::new(888), 50).await.unwrap();
assert_eq!(history.len(), 2);
}