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

View File

@@ -159,3 +159,36 @@ fn snapshot_chat_search_mode() {
let output = buffer_to_string(&buffer);
assert_snapshot!("chat_list_search_mode", output);
}
#[test]
fn snapshot_chat_with_online_status() {
use tele_tui::tdlib::UserOnlineStatus;
use tele_tui::types::ChatId;
let chat = TestChatBuilder::new("Alice", 123)
.last_message("Hey there!")
.build();
let mut app = TestAppBuilder::new()
.with_chat(chat)
.selected_chat(123)
.build();
// Устанавливаем онлайн-статус для чата напрямую
let chat_id = ChatId::new(123);
let user_id = tele_tui::types::UserId::new(123);
// Регистрируем чат как приватный
app.td_client.user_cache.register_private_chat(chat_id, user_id);
// Устанавливаем онлайн-статус
app.td_client.user_cache.user_statuses.insert(user_id, UserOnlineStatus::Online);
let buffer = render_to_buffer(80, 24, |f| {
tele_tui::ui::chat_list::render(f, f.area(), &mut app);
});
let output = buffer_to_string(&buffer);
assert_snapshot!("chat_with_online_status", output);
}

View File

@@ -4,74 +4,74 @@ mod helpers;
use helpers::fake_tdclient::FakeTdClient;
use helpers::test_data::TestMessageBuilder;
use tele_tui::types::MessageId;
use tele_tui::types::{ChatId, MessageId};
/// Test: Удаление сообщения убирает его из списка
#[test]
fn test_delete_message_removes_from_list() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_delete_message_removes_from_list() {
let client = FakeTdClient::new();
// Отправляем сообщение
let msg_id = client.send_message(123, "Delete me".to_string(), None);
let msg = client.send_message(ChatId::new(123), "Delete me".to_string(), None, None).await.unwrap();
// Проверяем что сообщение есть
assert_eq!(client.get_messages(123).len(), 1);
// Удаляем сообщение
client.delete_message(123, msg_id);
client.delete_messages(ChatId::new(123), vec![msg.id()], false).await.unwrap();
// Проверяем что удаление записалось
assert_eq!(client.deleted_messages().len(), 1);
assert_eq!(client.deleted_messages()[0], msg_id);
assert_eq!(client.get_deleted_messages().len(), 1);
assert_eq!(client.get_deleted_messages()[0].message_ids[0], msg.id());
// Проверяем что сообщение удалено из списка
assert_eq!(client.get_messages(123).len(), 0);
}
/// Test: Удаление нескольких сообщений
#[test]
fn test_delete_multiple_messages() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_delete_multiple_messages() {
let client = FakeTdClient::new();
// Отправляем 3 сообщения
let msg1_id = client.send_message(123, "Message 1".to_string(), None);
let msg2_id = client.send_message(123, "Message 2".to_string(), None);
let msg3_id = client.send_message(123, "Message 3".to_string(), None);
let msg1 = client.send_message(ChatId::new(123), "Message 1".to_string(), None, None).await.unwrap();
let msg2 = client.send_message(ChatId::new(123), "Message 2".to_string(), None, None).await.unwrap();
let msg3 = client.send_message(ChatId::new(123), "Message 3".to_string(), None, None).await.unwrap();
assert_eq!(client.get_messages(123).len(), 3);
// Удаляем первое и третье
client.delete_message(123, msg1_id);
client.delete_message(123, msg3_id);
client.delete_messages(ChatId::new(123), vec![msg1.id()], false).await.unwrap();
client.delete_messages(ChatId::new(123), vec![msg3.id()], false).await.unwrap();
// Проверяем историю удалений
assert_eq!(client.deleted_messages().len(), 2);
assert_eq!(client.deleted_messages()[0], msg1_id);
assert_eq!(client.deleted_messages()[1], msg3_id);
assert_eq!(client.get_deleted_messages().len(), 2);
assert_eq!(client.get_deleted_messages()[0].message_ids[0], msg1.id());
assert_eq!(client.get_deleted_messages()[1].message_ids[0], msg3.id());
// Проверяем что осталось только второе сообщение
let messages = client.get_messages(123);
assert_eq!(messages.len(), 1);
assert_eq!(messages[0].id(), MessageId::new(msg2_id));
assert_eq!(messages[0].id(), msg2.id());
assert_eq!(messages[0].content.text, "Message 2");
}
/// Test: Удаление только своих сообщений (проверка через can_be_deleted_for_all_users)
#[test]
fn test_can_only_delete_own_messages_for_all() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_can_only_delete_own_messages_for_all() {
let client = FakeTdClient::new();
// Наше исходящее сообщение (можно удалить для всех)
let outgoing_msg = TestMessageBuilder::new("My message", 1).outgoing().build();
client = client.with_message(123, outgoing_msg);
let client = client.with_message(123, outgoing_msg);
// Входящее сообщение от собеседника (можно удалить только для себя)
let incoming_msg = TestMessageBuilder::new("Their message", 2)
.sender("Alice")
.build();
client = client.with_message(123, incoming_msg);
let client = client.with_message(123, incoming_msg);
// Проверяем флаги удаления
let messages = client.get_messages(123);
@@ -84,55 +84,55 @@ fn test_can_only_delete_own_messages_for_all() {
}
/// Test: Удаление несуществующего сообщения (ничего не происходит)
#[test]
fn test_delete_nonexistent_message() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_delete_nonexistent_message() {
let client = FakeTdClient::new();
// Отправляем одно сообщение
let msg_id = client.send_message(123, "Exists".to_string(), None);
let msg = client.send_message(ChatId::new(123), "Exists".to_string(), None, None).await.unwrap();
assert_eq!(client.get_messages(123).len(), 1);
// Пытаемся удалить несуществующее
client.delete_message(123, 999);
client.delete_messages(ChatId::new(123), vec![MessageId::new(999)], false).await.unwrap();
// Удаление записалось в историю
assert_eq!(client.deleted_messages().len(), 1);
assert_eq!(client.deleted_messages()[0], 999);
assert_eq!(client.get_deleted_messages().len(), 1);
assert_eq!(client.get_deleted_messages()[0].message_ids[0], MessageId::new(999));
// Но существующее сообщение осталось
let messages = client.get_messages(123);
assert_eq!(messages.len(), 1);
assert_eq!(messages[0].id(), MessageId::new(msg_id));
assert_eq!(messages[0].id(), msg.id());
}
/// Test: Подтверждение удаления (симуляция модалки)
/// FakeTdClient сразу удаляет, но в реальном App должна быть модалка подтверждения
#[test]
fn test_delete_with_confirmation_flow() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_delete_with_confirmation_flow() {
let client = FakeTdClient::new();
let msg_id = client.send_message(123, "To delete".to_string(), None);
let msg = client.send_message(ChatId::new(123), "To delete".to_string(), None, None).await.unwrap();
// Шаг 1: Пользователь нажал 'd' -> показывается модалка (в App)
// В FakeTdClient просто проверяем что сообщение ещё есть
assert_eq!(client.get_messages(123).len(), 1);
assert_eq!(client.deleted_messages().len(), 0);
assert_eq!(client.get_deleted_messages().len(), 0);
// Шаг 2: Пользователь подтвердил 'y' -> удаляем
client.delete_message(123, msg_id);
client.delete_messages(ChatId::new(123), vec![msg.id()], false).await.unwrap();
// Проверяем что удалено
assert_eq!(client.get_messages(123).len(), 0);
assert_eq!(client.deleted_messages().len(), 1);
assert_eq!(client.get_deleted_messages().len(), 1);
}
/// Test: Отмена удаления (Esc) - сообщение остаётся
#[test]
fn test_cancel_delete_keeps_message() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_cancel_delete_keeps_message() {
let client = FakeTdClient::new();
let msg_id = client.send_message(123, "Keep me".to_string(), None);
let msg = client.send_message(ChatId::new(123), "Keep me".to_string(), None, None).await.unwrap();
// Шаг 1: Пользователь нажал 'd' -> показалась модалка
assert_eq!(client.get_messages(123).len(), 1);
@@ -141,10 +141,10 @@ fn test_cancel_delete_keeps_message() {
// Проверяем что сообщение осталось
assert_eq!(client.get_messages(123).len(), 1);
assert_eq!(client.deleted_messages().len(), 0);
assert_eq!(client.get_deleted_messages().len(), 0);
// Сообщение на месте
let messages = client.get_messages(123);
assert_eq!(messages[0].id(), MessageId::new(msg_id));
assert_eq!(messages[0].id(), msg.id());
assert_eq!(messages[0].content.text, "Keep me");
}

View File

@@ -3,6 +3,7 @@
mod helpers;
use helpers::test_data::{create_test_chat, TestChatBuilder};
use tele_tui::types::{ChatId, MessageId};
use std::collections::HashMap;
/// Простая структура для хранения черновиков (как в реальном App)
@@ -41,8 +42,8 @@ impl DraftManager {
}
/// Test: Переключение между чатами сохраняет текст
#[test]
fn test_switching_chats_saves_draft() {
#[tokio::test]
async fn test_switching_chats_saves_draft() {
let mut drafts = DraftManager::new();
// Пользователь в чате 123, начал печатать
@@ -64,8 +65,8 @@ fn test_switching_chats_saves_draft() {
}
/// Test: Возврат в чат восстанавливает текст
#[test]
fn test_returning_to_chat_restores_draft() {
#[tokio::test]
async fn test_returning_to_chat_restores_draft() {
let mut drafts = DraftManager::new();
// Сохраняем черновик в чате 123
@@ -82,8 +83,8 @@ fn test_returning_to_chat_restores_draft() {
}
/// Test: Отправка сообщения удаляет черновик
#[test]
fn test_sending_message_clears_draft() {
#[tokio::test]
async fn test_sending_message_clears_draft() {
let mut drafts = DraftManager::new();
// Сохранили черновик
@@ -99,8 +100,8 @@ fn test_sending_message_clears_draft() {
}
/// Test: Индикатор черновика в списке чатов
#[test]
fn test_draft_indicator_in_chat_list() {
#[tokio::test]
async fn test_draft_indicator_in_chat_list() {
let mut drafts = DraftManager::new();
// Создаём несколько чатов
@@ -126,8 +127,8 @@ fn test_draft_indicator_in_chat_list() {
}
/// Test: Множественные черновики в разных чатах
#[test]
fn test_multiple_drafts_in_different_chats() {
#[tokio::test]
async fn test_multiple_drafts_in_different_chats() {
let mut drafts = DraftManager::new();
// Создаём черновики в 3 чатах
@@ -150,8 +151,8 @@ fn test_multiple_drafts_in_different_chats() {
}
/// Test: Пустой текст не сохраняется как черновик
#[test]
fn test_empty_text_does_not_save_draft() {
#[tokio::test]
async fn test_empty_text_does_not_save_draft() {
let mut drafts = DraftManager::new();
// Пытаемся сохранить пустой черновик
@@ -172,8 +173,8 @@ fn test_empty_text_does_not_save_draft() {
}
/// Test: Редактирование черновика
#[test]
fn test_editing_draft() {
#[tokio::test]
async fn test_editing_draft() {
let mut drafts = DraftManager::new();
// Сохраняем начальный черновик

81
tests/e2e_smoke.rs Normal file
View File

@@ -0,0 +1,81 @@
// E2E Smoke tests для базовых сценариев запуска приложения
use tele_tui::tdlib::NetworkState;
/// Тест: Приложение запускается без краша
/// Проверяем что базовые структуры создаются корректно
#[tokio::test]
async fn test_app_starts_without_crash() {
// Проверяем что NetworkState enum работает корректно
let states = vec![
NetworkState::Ready,
NetworkState::WaitingForNetwork,
NetworkState::Connecting,
NetworkState::ConnectingToProxy,
NetworkState::Updating,
];
for state in states {
// Просто проверяем что состояния создаются без паники
let _text = match state {
NetworkState::Ready => "Ready",
NetworkState::WaitingForNetwork => "Waiting for network",
NetworkState::Connecting => "Connecting",
NetworkState::ConnectingToProxy => "Connecting to proxy",
NetworkState::Updating => "Updating",
};
}
}
/// Тест: Проверка минимального размера терминала
#[test]
fn test_minimum_terminal_size() {
const MIN_WIDTH: u16 = 80;
const MIN_HEIGHT: u16 = 20;
// Проверяем что константы установлены разумно
assert!(MIN_WIDTH >= 80, "Минимальная ширина должна быть >= 80");
assert!(MIN_HEIGHT >= 20, "Минимальная высота должна быть >= 20");
// Проверяем граничные случаи
let too_small_width = MIN_WIDTH - 1;
let too_small_height = MIN_HEIGHT - 1;
assert!(too_small_width < MIN_WIDTH);
assert!(too_small_height < MIN_HEIGHT);
}
/// Тест: Базовые константы приложения
#[test]
fn test_app_constants() {
use tele_tui::constants::*;
// Проверяем что лимиты установлены
assert!(MAX_MESSAGES_IN_CHAT > 0, "Лимит сообщений должен быть > 0");
assert!(MAX_CHATS > 0, "Лимит чатов должен быть > 0");
assert!(MAX_USER_CACHE_SIZE > 0, "Размер кэша пользователей должен быть > 0");
// Проверяем что лимиты разумные
assert!(MAX_MESSAGES_IN_CHAT <= 1000, "Слишком большой лимит сообщений");
assert!(MAX_CHATS <= 500, "Слишком большой лимит чатов");
}
/// Тест: Graceful shutdown флаг
#[test]
fn test_shutdown_flag() {
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
let shutdown = Arc::new(AtomicBool::new(false));
// Проверяем начальное состояние
assert!(!shutdown.load(Ordering::Relaxed), "Флаг должен быть false при создании");
// Проверяем установку флага
shutdown.store(true, Ordering::Relaxed);
assert!(shutdown.load(Ordering::Relaxed), "Флаг должен быть true после установки");
// Проверяем клонирование Arc
let shutdown_clone = Arc::clone(&shutdown);
assert!(shutdown_clone.load(Ordering::Relaxed), "Клон должен видеть то же значение");
}

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);
}

View File

@@ -4,36 +4,37 @@ mod helpers;
use helpers::fake_tdclient::FakeTdClient;
use helpers::test_data::TestMessageBuilder;
use tele_tui::types::{ChatId, MessageId};
/// Test: Редактирование сообщения изменяет текст
#[test]
fn test_edit_message_changes_text() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_edit_message_changes_text() {
let client = FakeTdClient::new();
// Отправляем сообщение
let msg_id = client.send_message(123, "Original text".to_string(), None);
let msg = client.send_message(ChatId::new(123), "Original text".to_string(), None, None).await.unwrap();
// Редактируем сообщение
client.edit_message(123, msg_id, "Edited text".to_string());
client.edit_message(ChatId::new(123), msg.id(), "Edited text".to_string()).await.unwrap();
// Проверяем что редактирование записалось
assert_eq!(client.edited_messages().len(), 1);
assert_eq!(client.edited_messages()[0].message_id, msg_id);
assert_eq!(client.edited_messages()[0].new_text, "Edited text");
assert_eq!(client.get_edited_messages().len(), 1);
assert_eq!(client.get_edited_messages()[0].message_id, msg.id());
assert_eq!(client.get_edited_messages()[0].new_text, "Edited text");
// Проверяем что текст сообщения изменился
let messages = client.get_messages(123);
assert_eq!(messages.len(), 1);
assert_eq!(messages[0].content.text, "Edited text");
assert_eq!(messages[0].text(), "Edited text");
}
/// Test: Редактирование устанавливает edit_date
#[test]
fn test_edit_message_sets_edit_date() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_edit_message_sets_edit_date() {
let client = FakeTdClient::new();
// Отправляем сообщение
let msg_id = client.send_message(123, "Original".to_string(), None);
let msg = client.send_message(ChatId::new(123), "Original".to_string(), None, None).await.unwrap();
// Получаем дату до редактирования
let messages_before = client.get_messages(123);
@@ -41,7 +42,7 @@ fn test_edit_message_sets_edit_date() {
assert_eq!(messages_before[0].edit_date(), 0); // Не редактировалось
// Редактируем сообщение
client.edit_message(123, msg_id, "Edited".to_string());
client.edit_message(ChatId::new(123), msg.id(), "Edited".to_string()).await.unwrap();
// Проверяем что edit_date установлена
let messages_after = client.get_messages(123);
@@ -50,21 +51,21 @@ fn test_edit_message_sets_edit_date() {
}
/// Test: Редактирование только своих сообщений (проверка через can_be_edited)
#[test]
fn test_can_only_edit_own_messages() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_can_only_edit_own_messages() {
let client = FakeTdClient::new();
// Наше исходящее сообщение (можно редактировать)
let outgoing_msg = TestMessageBuilder::new("My message", 1).outgoing().build();
client = client.with_message(123, outgoing_msg);
let client = client.with_message(123, outgoing_msg);
// Входящее сообщение от собеседника (нельзя редактировать)
let incoming_msg = TestMessageBuilder::new("Their message", 2)
.sender("Alice")
.build();
client = client.with_message(123, incoming_msg);
let client = client.with_message(123, incoming_msg);
// Проверяем флаги
let messages = client.get_messages(123);
@@ -73,56 +74,57 @@ fn test_can_only_edit_own_messages() {
}
/// Test: Множественные редактирования одного сообщения
#[test]
fn test_multiple_edits_of_same_message() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_multiple_edits_of_same_message() {
let client = FakeTdClient::new();
let msg_id = client.send_message(123, "Version 1".to_string(), None);
let msg = client.send_message(ChatId::new(123), "Version 1".to_string(), None, None).await.unwrap();
// Первое редактирование
client.edit_message(123, msg_id, "Version 2".to_string());
client.edit_message(ChatId::new(123), msg.id(), "Version 2".to_string()).await.unwrap();
// Второе редактирование
client.edit_message(123, msg_id, "Version 3".to_string());
client.edit_message(ChatId::new(123), msg.id(), "Version 3".to_string()).await.unwrap();
// Третье редактирование
client.edit_message(123, msg_id, "Final version".to_string());
client.edit_message(ChatId::new(123), msg.id(), "Final version".to_string()).await.unwrap();
// Проверяем что все 3 редактирования записаны
assert_eq!(client.edited_messages().len(), 3);
assert_eq!(client.edited_messages()[0].new_text, "Version 2");
assert_eq!(client.edited_messages()[1].new_text, "Version 3");
assert_eq!(client.edited_messages()[2].new_text, "Final version");
assert_eq!(client.get_edited_messages().len(), 3);
assert_eq!(client.get_edited_messages()[0].new_text, "Version 2");
assert_eq!(client.get_edited_messages()[1].new_text, "Version 3");
assert_eq!(client.get_edited_messages()[2].new_text, "Final version");
// Проверяем что сообщение содержит последнюю версию
let messages = client.get_messages(123);
assert_eq!(messages.len(), 1);
assert_eq!(messages[0].content.text, "Final version");
assert_eq!(messages[0].text(), "Final version");
}
/// Test: Редактирование несуществующего сообщения (ничего не происходит)
#[test]
fn test_edit_nonexistent_message() {
let mut client = FakeTdClient::new();
/// Test: Редактирование несуществующего сообщения (возвращает ошибку)
#[tokio::test]
async fn test_edit_nonexistent_message() {
let client = FakeTdClient::new();
// Пытаемся отредактировать несуществующее сообщение
client.edit_message(123, 999, "New text".to_string());
let result = client.edit_message(ChatId::new(123), MessageId::new(999), "New text".to_string()).await;
// Редактирование записалось в историю (FakeTdClient всё записывает)
assert_eq!(client.edited_messages().len(), 1);
// Должна вернуться ошибка
assert!(result.is_err());
assert_eq!(result.unwrap_err(), "Message not found");
// Но в списке сообщений ничего нет
// В списке сообщений ничего нет
let messages = client.get_messages(123);
assert_eq!(messages.len(), 0);
}
/// Test: Отмена редактирования (Esc) - тестируем что можно восстановить original
/// В данном случае проверяем что FakeTdClient сохраняет историю edits
#[test]
fn test_edit_history_tracking() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_edit_history_tracking() {
let client = FakeTdClient::new();
let msg_id = client.send_message(123, "Original".to_string(), None);
let msg = client.send_message(ChatId::new(123), "Original".to_string(), None, None).await.unwrap();
// Симулируем начало редактирования -> изменение -> отмена
// Отменять на уровне FakeTdClient нельзя, но можно проверить что original сохранён
@@ -132,19 +134,19 @@ fn test_edit_history_tracking() {
let original = messages_before[0].text().to_string();
// Редактируем
client.edit_message(123, msg_id, "Edited".to_string());
client.edit_message(ChatId::new(123), msg.id(), "Edited".to_string()).await.unwrap();
// Проверяем что изменилось
let messages_edited = client.get_messages(123);
assert_eq!(messages_edited[0].content.text, "Edited");
assert_eq!(messages_edited[0].text(), "Edited");
// Можем "отменить" редактирование вернув original
client.edit_message(123, msg_id, original);
client.edit_message(ChatId::new(123), msg.id(), original).await.unwrap();
// Проверяем что вернулось
let messages_restored = client.get_messages(123);
assert_eq!(messages_restored[0].content.text, "Original");
assert_eq!(messages_restored[0].text(), "Original");
// История показывает 2 редактирования
assert_eq!(client.edited_messages().len(), 2);
assert_eq!(client.get_edited_messages().len(), 2);
}

File diff suppressed because it is too large Load Diff

View File

@@ -4,17 +4,18 @@ mod helpers;
use helpers::fake_tdclient::FakeTdClient;
use helpers::test_data::{create_test_chat, TestMessageBuilder};
use tele_tui::types::{ChatId, MessageId};
/// Test: Навигация вверх/вниз по списку чатов
#[test]
fn test_navigate_chat_list_up_down() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_navigate_chat_list_up_down() {
let client = FakeTdClient::new();
let chat1 = create_test_chat("Mom", 123);
let chat2 = create_test_chat("Boss", 456);
let chat3 = create_test_chat("Friend", 789);
client = client.with_chats(vec![chat1, chat2, chat3]);
let client = client.with_chats(vec![chat1, chat2, chat3]);
let chats = client.get_chats();
@@ -52,9 +53,9 @@ fn test_navigate_chat_list_up_down() {
}
/// Test: Enter открывает чат
#[test]
fn test_enter_opens_chat() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_enter_opens_chat() {
let client = FakeTdClient::new();
let chat = create_test_chat("Mom", 123);
let _client = client.with_chat(chat);
@@ -70,8 +71,8 @@ fn test_enter_opens_chat() {
}
/// Test: Esc закрывает чат
#[test]
fn test_esc_closes_chat() {
#[tokio::test]
async fn test_esc_closes_chat() {
// Состояние: открыт чат 123
let selected_chat_id = Some(123);
@@ -82,9 +83,9 @@ fn test_esc_closes_chat() {
}
/// Test: Скролл сообщений в чате
#[test]
fn test_scroll_messages_in_chat() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_scroll_messages_in_chat() {
let client = FakeTdClient::new();
let messages = vec![
TestMessageBuilder::new("Msg 1", 1).build(),
@@ -94,7 +95,7 @@ fn test_scroll_messages_in_chat() {
TestMessageBuilder::new("Msg 5", 5).build(),
];
client = client.with_messages(123, messages);
let client = client.with_messages(123, messages);
let msgs = client.get_messages(123);
@@ -123,12 +124,12 @@ fn test_scroll_messages_in_chat() {
}
/// Test: Переключение между папками (1-9)
#[test]
fn test_switch_folders() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_switch_folders() {
let client = FakeTdClient::new();
// Добавляем папки (FakeTdClient уже создаёт "All" с id=0)
client = client.with_folder(1, "Personal").with_folder(2, "Work");
let client = client.with_folder(1, "Personal").with_folder(2, "Work");
let folders = client.get_folders();
@@ -156,8 +157,8 @@ fn test_switch_folders() {
}
/// Test: Русская раскладка для навигации (р/о/л/д)
#[test]
fn test_russian_layout_navigation() {
#[tokio::test]
async fn test_russian_layout_navigation() {
// В реальном App: к/j/h/l маппятся на р/о/л/д для русской раскладки
// Mapping:
@@ -181,9 +182,9 @@ fn test_russian_layout_navigation() {
}
/// Test: Подгрузка старых сообщений при скролле вверх
#[test]
fn test_load_older_messages_on_scroll_up() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_load_older_messages_on_scroll_up() {
let client = FakeTdClient::new();
// Начальные сообщения (последние 10)
let initial_messages = vec![
@@ -199,7 +200,7 @@ fn test_load_older_messages_on_scroll_up() {
TestMessageBuilder::new("Msg 100", 100).build(),
];
client = client.with_messages(123, initial_messages);
let client = client.with_messages(123, initial_messages);
assert_eq!(client.get_messages(123).len(), 10);
@@ -219,10 +220,11 @@ fn test_load_older_messages_on_scroll_up() {
let mut all_messages = older_messages;
all_messages.extend(client.get_messages(123));
client.messages.insert(123, all_messages);
let client = client.with_messages(123, all_messages);
// Теперь должно быть 15 сообщений
assert_eq!(client.get_messages(123).len(), 15);
assert_eq!(client.get_messages(123)[0].content.text, "Msg 81");
assert_eq!(client.get_messages(123)[14].content.text, "Msg 100");
let messages = client.get_messages(123);
assert_eq!(messages.len(), 15);
assert_eq!(messages[0].content.text, "Msg 81");
assert_eq!(messages[14].content.text, "Msg 100");
}

View File

@@ -5,44 +5,45 @@ mod helpers;
use helpers::fake_tdclient::FakeTdClient;
use helpers::test_data::create_test_chat;
use tele_tui::tdlib::NetworkState;
use tele_tui::types::ChatId;
/// Test: Смена состояния сети отображается в UI
#[test]
fn test_network_state_changes() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_network_state_changes() {
let client = FakeTdClient::new();
// Начальное состояние - Ready
assert_eq!(client.network_state, NetworkState::Ready);
assert_eq!(client.get_network_state(), NetworkState::Ready);
// Сеть пропала
client.network_state = NetworkState::WaitingForNetwork;
assert_eq!(client.network_state, NetworkState::WaitingForNetwork);
client.simulate_network_change(NetworkState::WaitingForNetwork);
assert_eq!(client.get_network_state(), NetworkState::WaitingForNetwork);
// В UI: "⚠ Нет сети"
// Подключаемся к прокси
client.network_state = NetworkState::ConnectingToProxy;
assert_eq!(client.network_state, NetworkState::ConnectingToProxy);
client.simulate_network_change(NetworkState::ConnectingToProxy);
assert_eq!(client.get_network_state(), NetworkState::ConnectingToProxy);
// В UI: "⏳ Прокси..."
// Подключаемся к серверам
client.network_state = NetworkState::Connecting;
assert_eq!(client.network_state, NetworkState::Connecting);
client.simulate_network_change(NetworkState::Connecting);
assert_eq!(client.get_network_state(), NetworkState::Connecting);
// В UI: "⏳ Подключение..."
// Соединение восстановлено
client.network_state = NetworkState::Ready;
assert_eq!(client.network_state, NetworkState::Ready);
client.simulate_network_change(NetworkState::Ready);
assert_eq!(client.get_network_state(), NetworkState::Ready);
// В UI: индикатор скрывается
}
/// Test: WaitingForNetwork - нет подключения
#[test]
fn test_network_waiting_for_network() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_network_waiting_for_network() {
let client = FakeTdClient::new();
client.network_state = NetworkState::WaitingForNetwork;
client.simulate_network_change(NetworkState::WaitingForNetwork);
assert_eq!(client.network_state, NetworkState::WaitingForNetwork);
assert_eq!(client.get_network_state(), NetworkState::WaitingForNetwork);
// В этом состоянии:
// - Показывается предупреждение "⚠ Нет сети"
@@ -51,78 +52,79 @@ fn test_network_waiting_for_network() {
}
/// Test: ConnectingToProxy - подключение через прокси
#[test]
fn test_network_connecting_to_proxy() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_network_connecting_to_proxy() {
let client = FakeTdClient::new();
client.network_state = NetworkState::ConnectingToProxy;
client.simulate_network_change(NetworkState::ConnectingToProxy);
assert_eq!(client.network_state, NetworkState::ConnectingToProxy);
assert_eq!(client.get_network_state(), NetworkState::ConnectingToProxy);
// В UI: "⏳ Прокси..."
}
/// Test: Connecting - подключение к серверам Telegram
#[test]
fn test_network_connecting() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_network_connecting() {
let client = FakeTdClient::new();
client.network_state = NetworkState::Connecting;
client.simulate_network_change(NetworkState::Connecting);
assert_eq!(client.network_state, NetworkState::Connecting);
assert_eq!(client.get_network_state(), NetworkState::Connecting);
// В UI: "⏳ Подключение..."
}
/// Test: Updating - обновление данных
#[test]
fn test_network_updating() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_network_updating() {
let client = FakeTdClient::new();
client.network_state = NetworkState::Updating;
client.simulate_network_change(NetworkState::Updating);
assert_eq!(client.network_state, NetworkState::Updating);
assert_eq!(client.get_network_state(), NetworkState::Updating);
// В UI: "⏳ Обновление..."
}
/// Test: Typing indicator - пользователь печатает
#[test]
fn test_typing_indicator_on() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_typing_indicator_on() {
let client = FakeTdClient::new();
let chat = create_test_chat("Alice", 123);
client = client.with_chat(chat);
let client = client.with_chat(chat);
// Alice начала печатать в чате 123
client.set_typing(Some(123));
// Симулируем через send_chat_action
client.send_chat_action(ChatId::new(123), "Typing".to_string()).await;
assert_eq!(client.typing_chat_id, Some(123));
assert_eq!(*client.typing_chat_id.lock().unwrap(), Some(123));
// В UI: под сообщениями отображается "Alice печатает..."
}
/// Test: Typing indicator - пользователь перестал печатать
#[test]
fn test_typing_indicator_off() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_typing_indicator_off() {
let client = FakeTdClient::new();
// Изначально Alice печатала
client.set_typing(Some(123));
assert_eq!(client.typing_chat_id, Some(123));
client.send_chat_action(ChatId::new(123), "Typing".to_string()).await;
assert_eq!(*client.typing_chat_id.lock().unwrap(), Some(123));
// Alice перестала печатать
client.set_typing(None);
client.send_chat_action(ChatId::new(123), "Cancel".to_string()).await;
assert_eq!(client.typing_chat_id, None);
assert_eq!(*client.typing_chat_id.lock().unwrap(), None);
// В UI: индикатор "печатает..." исчезает
}
/// Test: Отправка своего typing status
#[test]
fn test_send_own_typing_status() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_send_own_typing_status() {
let client = FakeTdClient::new();
// Пользователь начал печатать в чате 456
// В реальном App вызывается client.send_chat_action(chat_id, ChatAction::Typing)
@@ -142,9 +144,9 @@ fn test_send_own_typing_status() {
}
/// Test: Множественные переходы состояний сети
#[test]
fn test_multiple_network_state_transitions() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_multiple_network_state_transitions() {
let client = FakeTdClient::new();
// Цикл переходов состояний
let states = vec![
@@ -159,10 +161,10 @@ fn test_multiple_network_state_transitions() {
];
for state in states {
client.network_state = state.clone();
assert_eq!(client.network_state, state);
client.simulate_network_change(state.clone());
assert_eq!(client.get_network_state(), state);
}
// Финальное состояние - Ready
assert_eq!(client.network_state, NetworkState::Ready);
assert_eq!(client.get_network_state(), NetworkState::Ready);
}

View File

@@ -5,11 +5,11 @@ mod helpers;
use helpers::fake_tdclient::FakeTdClient;
use helpers::test_data::create_test_chat;
use tele_tui::tdlib::ProfileInfo;
use tele_tui::types::ChatId;
use tele_tui::types::{ChatId, MessageId};
/// Test: Открытие профиля в личном чате (i)
#[test]
fn test_open_profile_in_private_chat() {
#[tokio::test]
async fn test_open_profile_in_private_chat() {
let client = FakeTdClient::new();
let chat = create_test_chat("Alice", 123);
@@ -24,8 +24,8 @@ fn test_open_profile_in_private_chat() {
}
/// Test: Профиль показывает имя, username, телефон
#[test]
fn test_profile_shows_user_info() {
#[tokio::test]
async fn test_profile_shows_user_info() {
let profile = ProfileInfo {
chat_id: ChatId::new(123),
title: "Alice Johnson".to_string(),
@@ -47,8 +47,8 @@ fn test_profile_shows_user_info() {
}
/// Test: Профиль в группе показывает количество участников
#[test]
fn test_profile_shows_group_member_count() {
#[tokio::test]
async fn test_profile_shows_group_member_count() {
let profile = ProfileInfo {
chat_id: ChatId::new(456),
title: "Work Team".to_string(),
@@ -70,8 +70,8 @@ fn test_profile_shows_group_member_count() {
}
/// Test: Профиль в канале
#[test]
fn test_profile_shows_channel_info() {
#[tokio::test]
async fn test_profile_shows_channel_info() {
let profile = ProfileInfo {
chat_id: ChatId::new(789),
title: "News Channel".to_string(),
@@ -93,8 +93,8 @@ fn test_profile_shows_channel_info() {
}
/// Test: Закрытие профиля (Esc)
#[test]
fn test_close_profile_with_esc() {
#[tokio::test]
async fn test_close_profile_with_esc() {
// Профиль открыт
let profile_mode = true;
@@ -105,8 +105,8 @@ fn test_close_profile_with_esc() {
}
/// Test: Профиль без username и phone
#[test]
fn test_profile_without_optional_fields() {
#[tokio::test]
async fn test_profile_without_optional_fields() {
let profile = ProfileInfo {
chat_id: ChatId::new(999),
title: "Anonymous User".to_string(),

View File

@@ -4,89 +4,88 @@ mod helpers;
use helpers::fake_tdclient::FakeTdClient;
use helpers::test_data::TestMessageBuilder;
use tele_tui::types::ChatId;
/// Test: Добавление реакции к сообщению
#[test]
fn test_add_reaction_to_message() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_add_reaction_to_message() {
let client = FakeTdClient::new();
// Отправляем сообщение
let msg_id = client.send_message(123, "React to this!".to_string(), None);
let msg = client.send_message(ChatId::new(123), "React to this!".to_string(), None, None).await.unwrap();
// Добавляем реакцию
client.add_reaction(msg_id, "👍".to_string());
client.toggle_reaction(ChatId::new(123), msg.id(), "👍".to_string()).await.unwrap();
// Проверяем что реакция записалась
let reactions = client.reactions.get(&msg_id);
assert!(reactions.is_some());
assert_eq!(reactions.unwrap().len(), 1);
assert_eq!(reactions.unwrap()[0], "👍");
let messages = client.get_messages(123);
assert_eq!(messages.len(), 1);
assert_eq!(messages[0].reactions().len(), 1);
assert_eq!(messages[0].reactions()[0].emoji, "👍");
assert_eq!(messages[0].reactions()[0].count, 1);
assert_eq!(messages[0].reactions()[0].is_chosen, true);
}
/// Test: Удаление реакции (toggle) - вторичное нажатие
#[test]
fn test_toggle_reaction_removes_it() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_toggle_reaction_removes_it() {
let client = FakeTdClient::new();
// Создаём сообщение с нашей реакцией
let msg = TestMessageBuilder::new("Message", 100)
.reaction("👍", 1, true) // chosen=true - наша реакция
.build();
client = client.with_message(123, msg);
let client = client.with_message(123, msg);
// Проверяем что реакция есть
let messages_before = client.get_messages(123);
assert_eq!(messages_before[0].reactions().len(), 1);
assert_eq!(messages_before[0].reactions()[0].is_chosen, true);
// Симулируем удаление реакции (в реальном App это toggle)
// FakeTdClient просто записывает что реакция была "убрана"
// Для теста можем удалить из списка вручную или расширить FakeTdClient
let msg_id = messages_before[0].id();
// Создаём сообщение без реакции (после toggle)
let msg_after = TestMessageBuilder::new("Message", 100).build();
// Заменяем в клиенте
client.messages.insert(123, vec![msg_after]);
// Toggle - удаляем свою реакцию
client.toggle_reaction(ChatId::new(123), msg_id, "👍".to_string()).await.unwrap();
let messages_after = client.get_messages(123);
assert_eq!(messages_after[0].reactions().len(), 0);
}
/// Test: Множественные реакции на одно сообщение
#[test]
fn test_multiple_reactions_on_one_message() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_multiple_reactions_on_one_message() {
let client = FakeTdClient::new();
let msg_id = client.send_message(123, "Many reactions".to_string(), None);
let msg = client.send_message(ChatId::new(123), "Many reactions".to_string(), None, None).await.unwrap();
// Добавляем несколько разных реакций
client.add_reaction(msg_id, "👍".to_string());
client.add_reaction(msg_id, "❤️".to_string());
client.add_reaction(msg_id, "😂".to_string());
client.add_reaction(msg_id, "🔥".to_string());
client.toggle_reaction(ChatId::new(123), msg.id(), "👍".to_string()).await.unwrap();
client.toggle_reaction(ChatId::new(123), msg.id(), "❤️".to_string()).await.unwrap();
client.toggle_reaction(ChatId::new(123), msg.id(), "😂".to_string()).await.unwrap();
client.toggle_reaction(ChatId::new(123), msg.id(), "🔥".to_string()).await.unwrap();
// Проверяем что все 4 реакции записались
let reactions = client.reactions.get(&msg_id).unwrap();
let messages = client.get_messages(123);
let reactions = &messages[0].reactions();
assert_eq!(reactions.len(), 4);
assert_eq!(reactions[0], "👍");
assert_eq!(reactions[1], "❤️");
assert_eq!(reactions[2], "😂");
assert_eq!(reactions[3], "🔥");
assert_eq!(reactions[0].emoji, "👍");
assert_eq!(reactions[1].emoji, "❤️");
assert_eq!(reactions[2].emoji, "😂");
assert_eq!(reactions[3].emoji, "🔥");
}
/// Test: Реакции от разных пользователей (count > 1)
#[test]
fn test_reactions_from_multiple_users() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_reactions_from_multiple_users() {
let client = FakeTdClient::new();
// Создаём сообщение с реакцией от 3 пользователей
let msg = TestMessageBuilder::new("Popular message", 100)
.reaction("👍", 3, false) // 3 человека, но не мы
.build();
client = client.with_message(123, msg);
let client = client.with_message(123, msg);
let messages = client.get_messages(123);
let reaction = &messages[0].reactions()[0];
@@ -97,16 +96,16 @@ fn test_reactions_from_multiple_users() {
}
/// Test: Своя реакция (is_chosen = true)
#[test]
fn test_own_reaction_is_chosen() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_own_reaction_is_chosen() {
let client = FakeTdClient::new();
// Создаём сообщение с нашей реакцией
let msg = TestMessageBuilder::new("I reacted", 100)
.reaction("❤️", 1, true) // chosen=true
.build();
client = client.with_message(123, msg);
let client = client.with_message(123, msg);
let messages = client.get_messages(123);
let reaction = &messages[0].reactions()[0];
@@ -116,16 +115,16 @@ fn test_own_reaction_is_chosen() {
}
/// Test: Чужая реакция (is_chosen = false)
#[test]
fn test_other_reaction_not_chosen() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_other_reaction_not_chosen() {
let client = FakeTdClient::new();
// Создаём сообщение с чужой реакцией
let msg = TestMessageBuilder::new("They reacted", 100)
.reaction("😂", 2, false) // chosen=false
.build();
client = client.with_message(123, msg);
let client = client.with_message(123, msg);
let messages = client.get_messages(123);
let reaction = &messages[0].reactions()[0];
@@ -135,46 +134,50 @@ fn test_other_reaction_not_chosen() {
}
/// Test: Счётчик реакций увеличивается
#[test]
fn test_reaction_counter_increases() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_reaction_counter_increases() {
let client = FakeTdClient::new();
// Начальное сообщение с 1 реакцией
let msg_v1 = TestMessageBuilder::new("Growing", 100)
// Начальное сообщение с 1 реакцией от кого-то
let msg = TestMessageBuilder::new("Growing", 100)
.reaction("👍", 1, false)
.build();
client = client.with_message(123, msg_v1);
let client = client.with_message(123, msg);
// Симулируем обновление: теперь 5 человек
let msg_v2 = TestMessageBuilder::new("Growing", 100)
.reaction("👍", 5, false)
.build();
let messages_before = client.get_messages(123);
assert_eq!(messages_before[0].reactions()[0].count, 1);
client.messages.insert(123, vec![msg_v2]);
let msg_id = messages_before[0].id();
// Мы добавляем свою реакцию - счётчик должен увеличиться
client.toggle_reaction(ChatId::new(123), msg_id, "👍".to_string()).await.unwrap();
let messages = client.get_messages(123);
assert_eq!(messages[0].reactions()[0].count, 5);
assert_eq!(messages[0].reactions()[0].count, 2);
assert_eq!(messages[0].reactions()[0].is_chosen, true);
}
/// Test: Обновление реакции - мы добавили свою к существующим
#[test]
fn test_update_reaction_we_add_ours() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_update_reaction_we_add_ours() {
let client = FakeTdClient::new();
// Изначально: 2 человека, но не мы
let msg_before = TestMessageBuilder::new("Update", 100)
.reaction("🔥", 2, false)
.build();
client = client.with_message(123, msg_before);
let client = client.with_message(123, msg_before);
// После добавления нашей: 3 человека, в том числе мы
let msg_after = TestMessageBuilder::new("Update", 100)
.reaction("🔥", 3, true) // is_chosen=true теперь
.build();
let messages_before = client.get_messages(123);
assert_eq!(messages_before[0].reactions()[0].count, 2);
assert_eq!(messages_before[0].reactions()[0].is_chosen, false);
client.messages.insert(123, vec![msg_after]);
let msg_id = messages_before[0].id();
// Добавляем нашу реакцию
client.toggle_reaction(ChatId::new(123), msg_id, "🔥".to_string()).await.unwrap();
let messages = client.get_messages(123);
let reaction = &messages[0].reactions()[0];
@@ -184,15 +187,15 @@ fn test_update_reaction_we_add_ours() {
}
/// Test: Реакция с count=1 отображается только emoji
#[test]
fn test_single_reaction_shows_only_emoji() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_single_reaction_shows_only_emoji() {
let client = FakeTdClient::new();
let msg = TestMessageBuilder::new("Single", 100)
.reaction("❤️", 1, true)
.build();
client = client.with_message(123, msg);
let client = client.with_message(123, msg);
let messages = client.get_messages(123);
let reaction = &messages[0].reactions()[0];
@@ -203,9 +206,9 @@ fn test_single_reaction_shows_only_emoji() {
}
/// Test: Реакции на несколько сообщений
#[test]
fn test_reactions_on_multiple_messages() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_reactions_on_multiple_messages() {
let client = FakeTdClient::new();
let msg1 = TestMessageBuilder::new("First", 100)
.reaction("👍", 2, false)
@@ -220,7 +223,7 @@ fn test_reactions_on_multiple_messages() {
.reaction("🔥", 3, true) // Две разные реакции
.build();
client = client
let client = client
.with_message(123, msg1)
.with_message(123, msg2)
.with_message(123, msg3);

View File

@@ -5,38 +5,45 @@ mod helpers;
use helpers::fake_tdclient::FakeTdClient;
use helpers::test_data::TestMessageBuilder;
use tele_tui::tdlib::{ForwardInfo, ReplyInfo};
use tele_tui::types::MessageId;
use tele_tui::types::{ChatId, MessageId};
/// Test: Reply создаёт сообщение с reply_to
#[test]
fn test_reply_creates_message_with_reply_to() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_reply_creates_message_with_reply_to() {
let client = FakeTdClient::new();
// Входящее сообщение от собеседника
let original_msg = TestMessageBuilder::new("Question?", 100)
.sender("Alice")
.build();
client = client.with_message(123, original_msg);
let client = client.with_message(123, original_msg);
// Создаём reply info
let reply_info = ReplyInfo {
message_id: MessageId::new(100),
sender_name: "Alice".to_string(),
text: "Question?".to_string(),
};
// Отвечаем на него
let reply_id = client.send_message(123, "Answer!".to_string(), Some(100));
let reply_msg = client.send_message(ChatId::new(123), "Answer!".to_string(), Some(MessageId::new(100)), Some(reply_info)).await.unwrap();
// Проверяем что ответ отправлен с reply_to
assert_eq!(client.sent_messages().len(), 1);
assert_eq!(client.sent_messages()[0].reply_to, Some(100));
assert_eq!(client.get_sent_messages().len(), 1);
assert_eq!(client.get_sent_messages()[0].reply_to, Some(MessageId::new(100)));
// Проверяем что в списке 2 сообщения
let messages = client.get_messages(123);
assert_eq!(messages.len(), 2);
assert_eq!(messages[1].id(), MessageId::new(reply_id));
assert_eq!(messages[1].id(), reply_msg.id());
assert_eq!(messages[1].content.text, "Answer!");
}
/// Test: Reply отображает превью оригинального сообщения
#[test]
fn test_reply_shows_original_preview() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_reply_shows_original_preview() {
let client = FakeTdClient::new();
// Создаём сообщение с reply info
let reply_msg = TestMessageBuilder::new("Reply text", 101)
@@ -44,7 +51,7 @@ fn test_reply_shows_original_preview() {
.reply_to(100, "Alice", "Original")
.build();
client = client.with_message(123, reply_msg);
let client = client.with_message(123, reply_msg);
// Проверяем что reply_to сохранено
let messages = client.get_messages(123);
@@ -58,39 +65,39 @@ fn test_reply_shows_original_preview() {
}
/// Test: Отмена reply mode (Esc) - сообщение отправляется без reply_to
#[test]
fn test_cancel_reply_sends_without_reply_to() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_cancel_reply_sends_without_reply_to() {
let client = FakeTdClient::new();
// Входящее сообщение
let original = TestMessageBuilder::new("Question?", 100)
.sender("Alice")
.build();
client = client.with_message(123, original);
let client = client.with_message(123, original);
// Пользователь начал reply (r), потом отменил (Esc), затем отправил
// Это эмулируется отправкой без reply_to
client.send_message(123, "Regular message".to_string(), None);
client.send_message(ChatId::new(123), "Regular message".to_string(), None, None).await.unwrap();
// Проверяем что отправилось без reply_to
assert_eq!(client.sent_messages()[0].reply_to, None);
assert_eq!(client.get_sent_messages()[0].reply_to, None);
let messages = client.get_messages(123);
assert_eq!(messages[1].content.text, "Regular message");
}
/// Test: Forward создаёт сообщение с forward_from
#[test]
fn test_forward_creates_message_with_forward_from() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_forward_creates_message_with_forward_from() {
let client = FakeTdClient::new();
// Создаём пересланное сообщение
let forwarded_msg = TestMessageBuilder::new("Forwarded text", 200)
.forwarded_from("Bob")
.build();
client = client.with_message(456, forwarded_msg);
let client = client.with_message(456, forwarded_msg);
// Проверяем что forward_from сохранено
let messages = client.get_messages(456);
@@ -104,15 +111,15 @@ fn test_forward_creates_message_with_forward_from() {
/// Test: Forward показывает "↪ Переслано от ..."
/// Проверяем что у пересланного сообщения есть forward_from
#[test]
fn test_forward_displays_sender_name() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_forward_displays_sender_name() {
let client = FakeTdClient::new();
let msg = TestMessageBuilder::new("Important info", 300)
.forwarded_from("Charlie")
.build();
client = client.with_message(789, msg);
let client = client.with_message(789, msg);
let messages = client.get_messages(789);
let forward = messages[0].forward_from().unwrap();
@@ -122,23 +129,23 @@ fn test_forward_displays_sender_name() {
}
/// Test: Forward в другой чат
#[test]
fn test_forward_to_different_chat() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_forward_to_different_chat() {
let client = FakeTdClient::new();
// Исходное сообщение в чате 123
let original = TestMessageBuilder::new("Share this", 100)
.sender("Alice")
.build();
client = client.with_message(123, original);
let client = client.with_message(123, original);
// Пересылаем в чат 456
let forwarded = TestMessageBuilder::new("Share this", 101)
.forwarded_from("Alice")
.build();
client = client.with_message(456, forwarded);
let client = client.with_message(456, forwarded);
// Проверяем что в первом чате 1 сообщение
assert_eq!(client.get_messages(123).len(), 1);
@@ -149,32 +156,39 @@ fn test_forward_to_different_chat() {
}
/// Test: Reply + Forward комбинация (ответ на пересланное сообщение)
#[test]
fn test_reply_to_forwarded_message() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_reply_to_forwarded_message() {
let client = FakeTdClient::new();
// Пересланное сообщение
let forwarded = TestMessageBuilder::new("Forwarded", 100)
.forwarded_from("Bob")
.build();
client = client.with_message(123, forwarded);
let client = client.with_message(123, forwarded);
// Создаём reply info
let reply_info = ReplyInfo {
message_id: MessageId::new(100),
sender_name: "Bob".to_string(),
text: "Forwarded".to_string(),
};
// Отвечаем на пересланное сообщение
let reply_id = client.send_message(123, "Thanks for sharing!".to_string(), Some(100));
let reply_msg = client.send_message(ChatId::new(123), "Thanks for sharing!".to_string(), Some(MessageId::new(100)), Some(reply_info)).await.unwrap();
// Проверяем что reply содержит reply_to
assert_eq!(client.sent_messages()[0].reply_to, Some(100));
assert_eq!(client.get_sent_messages()[0].reply_to, Some(MessageId::new(100)));
let messages = client.get_messages(123);
assert_eq!(messages.len(), 2);
assert_eq!(messages[1].id(), MessageId::new(reply_id));
assert_eq!(messages[1].id(), reply_msg.id());
}
/// Test: Forward множества сообщений (batch forward)
#[test]
fn test_forward_multiple_messages() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_forward_multiple_messages() {
let client = FakeTdClient::new();
// Создаём 3 пересланных сообщения
let msg1 = TestMessageBuilder::new("Message 1", 100)
@@ -189,7 +203,7 @@ fn test_forward_multiple_messages() {
.forwarded_from("Alice")
.build();
client = client
let client = client
.with_message(456, msg1)
.with_message(456, msg2)
.with_message(456, msg3);

View File

@@ -4,22 +4,23 @@ mod helpers;
use helpers::fake_tdclient::FakeTdClient;
use helpers::test_data::{create_test_chat, TestChatBuilder, TestMessageBuilder};
use tele_tui::types::{ChatId, MessageId};
/// Test: Поиск по чатам фильтрует по названию
#[test]
fn test_search_chats_by_title() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_search_chats_by_title() {
let client = FakeTdClient::new();
let chat1 = create_test_chat("Mom", 123);
let chat2 = create_test_chat("Boss", 456);
let chat3 = create_test_chat("Mom's Work", 789);
client = client.with_chats(vec![chat1, chat2, chat3]);
let client = client.with_chats(vec![chat1, chat2, chat3]);
// Ищем "mom" - должно найти "Mom" и "Mom's Work"
let query = "mom".to_lowercase();
let filtered: Vec<_> = client
.get_chats()
let chats = client.get_chats();
let filtered: Vec<_> = chats
.iter()
.filter(|c| c.title.to_lowercase().contains(&query))
.collect();
@@ -30,9 +31,9 @@ fn test_search_chats_by_title() {
}
/// Test: Поиск по чатам фильтрует по @username
#[test]
fn test_search_chats_by_username() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_search_chats_by_username() {
let client = FakeTdClient::new();
let chat1 = TestChatBuilder::new("Alice", 123).username("alice").build();
@@ -40,12 +41,12 @@ fn test_search_chats_by_username() {
let chat3 = TestChatBuilder::new("Charlie", 789).build(); // Без username
client = client.with_chats(vec![chat1, chat2, chat3]);
let client = client.with_chats(vec![chat1, chat2, chat3]);
// Ищем "bob" - должно найти "Bob" (@bobby)
let query = "bob".to_lowercase();
let filtered: Vec<_> = client
.get_chats()
let chats = client.get_chats();
let filtered: Vec<_> = chats
.iter()
.filter(|c| {
c.title.to_lowercase().contains(&query)
@@ -61,20 +62,20 @@ fn test_search_chats_by_username() {
}
/// Test: Пустой поисковый запрос возвращает все чаты
#[test]
fn test_search_empty_query_returns_all() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_search_empty_query_returns_all() {
let client = FakeTdClient::new();
let chat1 = create_test_chat("Mom", 123);
let chat2 = create_test_chat("Boss", 456);
let chat3 = create_test_chat("Friend", 789);
client = client.with_chats(vec![chat1, chat2, chat3]);
let client = client.with_chats(vec![chat1, chat2, chat3]);
// Пустой запрос
let query = "";
let filtered: Vec<_> = client
.get_chats()
let chats = client.get_chats();
let filtered: Vec<_> = chats
.iter()
.filter(|c| c.title.to_lowercase().contains(query))
.collect();
@@ -84,15 +85,15 @@ fn test_search_empty_query_returns_all() {
}
/// Test: Поиск внутри чата по тексту сообщений
#[test]
fn test_search_messages_in_chat() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_search_messages_in_chat() {
let client = FakeTdClient::new();
let msg1 = TestMessageBuilder::new("Hello world", 100).build();
let msg2 = TestMessageBuilder::new("How are you?", 101).build();
let msg3 = TestMessageBuilder::new("Hello again", 102).build();
client = client.with_messages(123, vec![msg1, msg2, msg3]);
let client = client.with_messages(123, vec![msg1, msg2, msg3]);
// Ищем "hello"
let query = "hello".to_lowercase();
@@ -108,15 +109,15 @@ fn test_search_messages_in_chat() {
}
/// Test: Навигация по результатам поиска (n/N)
#[test]
fn test_navigate_search_results() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_navigate_search_results() {
let client = FakeTdClient::new();
let msg1 = TestMessageBuilder::new("First match", 100).build();
let msg2 = TestMessageBuilder::new("Second match", 101).build();
let msg3 = TestMessageBuilder::new("Third match", 102).build();
client = client.with_messages(123, vec![msg1, msg2, msg3]);
let client = client.with_messages(123, vec![msg1, msg2, msg3]);
// Ищем "match"
let query = "match".to_lowercase();
@@ -158,15 +159,15 @@ fn test_navigate_search_results() {
}
/// Test: Поиск с учётом регистра (case-insensitive)
#[test]
fn test_search_case_insensitive() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_search_case_insensitive() {
let client = FakeTdClient::new();
let msg1 = TestMessageBuilder::new("HELLO", 100).build();
let msg2 = TestMessageBuilder::new("hello", 101).build();
let msg3 = TestMessageBuilder::new("HeLLo", 102).build();
client = client.with_messages(123, vec![msg1, msg2, msg3]);
let client = client.with_messages(123, vec![msg1, msg2, msg3]);
// Ищем "hello" (lowercase)
let query = "hello".to_lowercase();
@@ -181,14 +182,14 @@ fn test_search_case_insensitive() {
}
/// Test: Поиск не находит ничего
#[test]
fn test_search_no_results() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_search_no_results() {
let client = FakeTdClient::new();
let msg1 = TestMessageBuilder::new("Hello", 100).build();
let msg2 = TestMessageBuilder::new("World", 101).build();
client = client.with_messages(123, vec![msg1, msg2]);
let client = client.with_messages(123, vec![msg1, msg2]);
// Ищем "xyz" - не должно найтись
let query = "xyz".to_lowercase();
@@ -202,14 +203,14 @@ fn test_search_no_results() {
}
/// Test: Отмена поиска (Esc) восстанавливает обычный режим
#[test]
fn test_cancel_search_restores_normal_mode() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_cancel_search_restores_normal_mode() {
let client = FakeTdClient::new();
let chat1 = create_test_chat("Mom", 123);
let chat2 = create_test_chat("Boss", 456);
client = client.with_chats(vec![chat1, chat2]);
let client = client.with_chats(vec![chat1, chat2]);
// Симулируем: пользователь начал поиск
let mut is_searching = true;
@@ -217,8 +218,8 @@ fn test_cancel_search_restores_normal_mode() {
// Фильтруем
let query = search_query.to_lowercase();
let filtered: Vec<_> = client
.get_chats()
let chats = client.get_chats();
let filtered: Vec<_> = chats
.iter()
.filter(|c| c.title.to_lowercase().contains(&query))
.collect();

View File

@@ -4,54 +4,55 @@ mod helpers;
use helpers::fake_tdclient::FakeTdClient;
use helpers::test_data::{create_test_chat, TestMessageBuilder};
use tele_tui::types::ChatId;
/// Test: Отправка текстового сообщения
#[test]
fn test_send_text_message() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_send_text_message() {
let client = FakeTdClient::new();
let chat = create_test_chat("Mom", 123);
client = client.with_chat(chat);
let client = client.with_chat(chat);
// Отправляем сообщение
let msg_id = client.send_message(123, "Hello, Mom!".to_string(), None);
let msg = client.send_message(ChatId::new(123), "Hello, Mom!".to_string(), None, None).await.unwrap();
// Проверяем что сообщение было отправлено
assert_eq!(client.sent_messages().len(), 1);
assert_eq!(client.sent_messages()[0].chat_id, 123);
assert_eq!(client.sent_messages()[0].text, "Hello, Mom!");
assert_eq!(client.sent_messages()[0].reply_to, None);
assert_eq!(client.get_sent_messages().len(), 1);
assert_eq!(client.get_sent_messages()[0].chat_id, 123);
assert_eq!(client.get_sent_messages()[0].text, "Hello, Mom!");
assert_eq!(client.get_sent_messages()[0].reply_to, None);
// Проверяем что сообщение добавилось в список
let messages = client.get_messages(123);
assert_eq!(messages.len(), 1);
assert_eq!(messages[0].id().as_i64(), msg_id);
assert_eq!(messages[0].id(), msg.id());
assert_eq!(messages[0].text(), "Hello, Mom!");
assert_eq!(messages[0].is_outgoing(), true);
}
/// Test: Отправка нескольких сообщений обновляет список
#[test]
fn test_send_multiple_messages_updates_list() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_send_multiple_messages_updates_list() {
let client = FakeTdClient::new();
// Отправляем первое сообщение
let msg1_id = client.send_message(123, "Message 1".to_string(), None);
let msg1 = client.send_message(ChatId::new(123), "Message 1".to_string(), None, None).await.unwrap();
// Отправляем второе сообщение
let msg2_id = client.send_message(123, "Message 2".to_string(), None);
let msg2 = client.send_message(ChatId::new(123), "Message 2".to_string(), None, None).await.unwrap();
// Отправляем третье сообщение
let msg3_id = client.send_message(123, "Message 3".to_string(), None);
let msg3 = client.send_message(ChatId::new(123), "Message 3".to_string(), None, None).await.unwrap();
// Проверяем что все 3 сообщения отслеживаются
assert_eq!(client.sent_messages().len(), 3);
assert_eq!(client.get_sent_messages().len(), 3);
// Проверяем что все сообщения в списке
let messages = client.get_messages(123);
assert_eq!(messages.len(), 3);
assert_eq!(messages[0].id().as_i64(), msg1_id);
assert_eq!(messages[1].id().as_i64(), msg2_id);
assert_eq!(messages[2].id().as_i64(), msg3_id);
assert_eq!(messages[0].id(), msg1.id());
assert_eq!(messages[1].id(), msg2.id());
assert_eq!(messages[2].id(), msg3.id());
assert_eq!(messages[0].text(), "Message 1");
assert_eq!(messages[1].text(), "Message 2");
assert_eq!(messages[2].text(), "Message 3");
@@ -60,31 +61,31 @@ fn test_send_multiple_messages_updates_list() {
/// Test: Отправка пустого сообщения (должно быть игнорировано на уровне App)
/// Здесь мы тестируем что FakeTdClient технически может отправить пустое сообщение,
/// но в реальном App это должно фильтроваться
#[test]
fn test_send_empty_message_technical() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_send_empty_message_technical() {
let client = FakeTdClient::new();
// FakeTdClient технически может отправить пустое сообщение
let msg_id = client.send_message(123, "".to_string(), None);
let msg = client.send_message(ChatId::new(123), "".to_string(), None, None).await.unwrap();
// Проверяем что оно отправилось (в реальном App это должно фильтроваться)
assert_eq!(client.sent_messages().len(), 1);
assert_eq!(client.sent_messages()[0].text, "");
assert_eq!(client.get_sent_messages().len(), 1);
assert_eq!(client.get_sent_messages()[0].text, "");
let messages = client.get_messages(123);
assert_eq!(messages.len(), 1);
assert_eq!(messages[0].id().as_i64(), msg_id);
assert_eq!(messages[0].id(), msg.id());
assert_eq!(messages[0].text(), "");
}
/// Test: Отправка сообщения с форматированием (markdown сущности)
/// В данном случае мы не проверяем парсинг markdown, только что текст сохраняется
#[test]
fn test_send_message_with_markdown() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_send_message_with_markdown() {
let client = FakeTdClient::new();
let text = "**Bold** *italic* `code`";
client.send_message(123, text.to_string(), None);
client.send_message(ChatId::new(123), text.to_string(), None, None).await.unwrap();
// Проверяем что текст сохранился как есть (парсинг markdown - отдельная логика)
let messages = client.get_messages(123);
@@ -93,21 +94,21 @@ fn test_send_message_with_markdown() {
}
/// Test: Отправка сообщения в разные чаты
#[test]
fn test_send_messages_to_different_chats() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_send_messages_to_different_chats() {
let client = FakeTdClient::new();
// Отправляем в чат 123
client.send_message(123, "Hello Mom".to_string(), None);
client.send_message(ChatId::new(123), "Hello Mom".to_string(), None, None).await.unwrap();
// Отправляем в чат 456
client.send_message(456, "Hello Boss".to_string(), None);
client.send_message(ChatId::new(456), "Hello Boss".to_string(), None, None).await.unwrap();
// Отправляем ещё одно в чат 123
client.send_message(123, "How are you?".to_string(), None);
client.send_message(ChatId::new(123), "How are you?".to_string(), None, None).await.unwrap();
// Проверяем общее количество отправленных
assert_eq!(client.sent_messages().len(), 3);
assert_eq!(client.get_sent_messages().len(), 3);
// Проверяем что сообщения распределены по чатам
let chat123_messages = client.get_messages(123);
@@ -122,19 +123,19 @@ fn test_send_messages_to_different_chats() {
/// Test: Новое сообщение появляется в реальном времени (симуляция)
/// Тестируем что когда приходит новое входящее сообщение, оно добавляется в список
#[test]
fn test_receive_incoming_message() {
let mut client = FakeTdClient::new();
#[tokio::test]
async fn test_receive_incoming_message() {
let client = FakeTdClient::new();
// Добавляем существующее сообщение
client.send_message(123, "My outgoing".to_string(), None);
client.send_message(ChatId::new(123), "My outgoing".to_string(), None, None).await.unwrap();
// Симулируем входящее сообщение от собеседника
let incoming_msg = TestMessageBuilder::new("Hey there!", 2000)
.sender("Alice")
.build();
client = client.with_message(123, incoming_msg);
let client = client.with_message(123, incoming_msg);
// Проверяем что в списке 2 сообщения
let messages = client.get_messages(123);

View File

@@ -0,0 +1,28 @@
---
source: tests/chat_list.rs
expression: output
---
┌──────────────────────────────────────────────────────────────────────────────┐
│🔍 Ctrl+S для поиска │
└──────────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────────┐
│▌● Alice │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
└──────────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────────┐
│● онлайн │
└──────────────────────────────────────────────────────────────────────────────┘