fixes
This commit is contained in:
260
tests/config.rs
Normal file
260
tests/config.rs
Normal file
@@ -0,0 +1,260 @@
|
||||
// Integration tests for config flow
|
||||
|
||||
use tele_tui::config::{Config, ColorsConfig, GeneralConfig};
|
||||
|
||||
/// Test: Дефолтные значения конфигурации
|
||||
#[test]
|
||||
fn test_config_default_values() {
|
||||
let config = Config::default();
|
||||
|
||||
// Проверяем дефолтный timezone
|
||||
assert_eq!(config.general.timezone, "+03:00");
|
||||
|
||||
// Проверяем дефолтные цвета
|
||||
assert_eq!(config.colors.incoming_message, "white");
|
||||
assert_eq!(config.colors.outgoing_message, "green");
|
||||
assert_eq!(config.colors.selected_message, "yellow");
|
||||
assert_eq!(config.colors.reaction_chosen, "yellow");
|
||||
assert_eq!(config.colors.reaction_other, "gray");
|
||||
}
|
||||
|
||||
/// Test: Создание конфига с кастомными значениями
|
||||
#[test]
|
||||
fn test_config_custom_values() {
|
||||
let config = Config {
|
||||
general: GeneralConfig {
|
||||
timezone: "+05:00".to_string(),
|
||||
},
|
||||
colors: ColorsConfig {
|
||||
incoming_message: "cyan".to_string(),
|
||||
outgoing_message: "blue".to_string(),
|
||||
selected_message: "red".to_string(),
|
||||
reaction_chosen: "green".to_string(),
|
||||
reaction_other: "white".to_string(),
|
||||
},
|
||||
};
|
||||
|
||||
assert_eq!(config.general.timezone, "+05:00");
|
||||
assert_eq!(config.colors.incoming_message, "cyan");
|
||||
assert_eq!(config.colors.outgoing_message, "blue");
|
||||
}
|
||||
|
||||
/// Test: Парсинг валидных цветов
|
||||
#[test]
|
||||
fn test_parse_valid_colors() {
|
||||
use ratatui::style::Color;
|
||||
|
||||
let config = Config::default();
|
||||
|
||||
assert_eq!(config.parse_color("red"), Color::Red);
|
||||
assert_eq!(config.parse_color("green"), Color::Green);
|
||||
assert_eq!(config.parse_color("blue"), Color::Blue);
|
||||
assert_eq!(config.parse_color("yellow"), Color::Yellow);
|
||||
assert_eq!(config.parse_color("cyan"), Color::Cyan);
|
||||
assert_eq!(config.parse_color("magenta"), Color::Magenta);
|
||||
assert_eq!(config.parse_color("white"), Color::White);
|
||||
assert_eq!(config.parse_color("black"), Color::Black);
|
||||
assert_eq!(config.parse_color("gray"), Color::Gray);
|
||||
assert_eq!(config.parse_color("grey"), Color::Gray);
|
||||
}
|
||||
|
||||
/// Test: Парсинг light цветов
|
||||
#[test]
|
||||
fn test_parse_light_colors() {
|
||||
use ratatui::style::Color;
|
||||
|
||||
let config = Config::default();
|
||||
|
||||
assert_eq!(config.parse_color("lightred"), Color::LightRed);
|
||||
assert_eq!(config.parse_color("lightgreen"), Color::LightGreen);
|
||||
assert_eq!(config.parse_color("lightblue"), Color::LightBlue);
|
||||
assert_eq!(config.parse_color("lightyellow"), Color::LightYellow);
|
||||
assert_eq!(config.parse_color("lightcyan"), Color::LightCyan);
|
||||
assert_eq!(config.parse_color("lightmagenta"), Color::LightMagenta);
|
||||
}
|
||||
|
||||
/// Test: Парсинг невалидного цвета использует fallback (White)
|
||||
#[test]
|
||||
fn test_parse_invalid_color_fallback() {
|
||||
use ratatui::style::Color;
|
||||
|
||||
let config = Config::default();
|
||||
|
||||
// Невалидные цвета должны возвращать White
|
||||
assert_eq!(config.parse_color("invalid_color"), Color::White);
|
||||
assert_eq!(config.parse_color(""), Color::White);
|
||||
assert_eq!(config.parse_color("purple"), Color::White); // purple не поддерживается
|
||||
assert_eq!(config.parse_color("Orange"), Color::White); // orange не поддерживается
|
||||
}
|
||||
|
||||
/// Test: Case-insensitive парсинг цветов
|
||||
#[test]
|
||||
fn test_parse_color_case_insensitive() {
|
||||
use ratatui::style::Color;
|
||||
|
||||
let config = Config::default();
|
||||
|
||||
assert_eq!(config.parse_color("RED"), Color::Red);
|
||||
assert_eq!(config.parse_color("Green"), Color::Green);
|
||||
assert_eq!(config.parse_color("BLUE"), Color::Blue);
|
||||
assert_eq!(config.parse_color("YeLLoW"), Color::Yellow);
|
||||
}
|
||||
|
||||
/// Test: Сериализация и десериализация TOML
|
||||
#[test]
|
||||
fn test_config_toml_serialization() {
|
||||
let original_config = Config {
|
||||
general: GeneralConfig {
|
||||
timezone: "-05:00".to_string(),
|
||||
},
|
||||
colors: ColorsConfig {
|
||||
incoming_message: "cyan".to_string(),
|
||||
outgoing_message: "blue".to_string(),
|
||||
selected_message: "red".to_string(),
|
||||
reaction_chosen: "green".to_string(),
|
||||
reaction_other: "white".to_string(),
|
||||
},
|
||||
};
|
||||
|
||||
// Сериализуем в TOML
|
||||
let toml_string = toml::to_string(&original_config).expect("Failed to serialize config");
|
||||
|
||||
// Десериализуем обратно
|
||||
let deserialized: Config = toml::from_str(&toml_string).expect("Failed to deserialize config");
|
||||
|
||||
// Проверяем что всё совпадает
|
||||
assert_eq!(deserialized.general.timezone, "-05:00");
|
||||
assert_eq!(deserialized.colors.incoming_message, "cyan");
|
||||
assert_eq!(deserialized.colors.outgoing_message, "blue");
|
||||
assert_eq!(deserialized.colors.selected_message, "red");
|
||||
}
|
||||
|
||||
/// Test: Парсинг TOML с частичными данными использует дефолты
|
||||
#[test]
|
||||
fn test_config_partial_toml_uses_defaults() {
|
||||
// TOML только с timezone, без colors
|
||||
let toml_str = r#"
|
||||
[general]
|
||||
timezone = "+02:00"
|
||||
"#;
|
||||
|
||||
let config: Config = toml::from_str(toml_str).expect("Failed to parse partial TOML");
|
||||
|
||||
// Timezone должен быть из TOML
|
||||
assert_eq!(config.general.timezone, "+02:00");
|
||||
|
||||
// Colors должны быть дефолтными
|
||||
assert_eq!(config.colors.incoming_message, "white");
|
||||
assert_eq!(config.colors.outgoing_message, "green");
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod timezone_tests {
|
||||
use super::*;
|
||||
|
||||
/// Test: Различные форматы timezone
|
||||
#[test]
|
||||
fn test_timezone_formats() {
|
||||
let positive = Config {
|
||||
general: GeneralConfig {
|
||||
timezone: "+03:00".to_string(),
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
assert_eq!(positive.general.timezone, "+03:00");
|
||||
|
||||
let negative = Config {
|
||||
general: GeneralConfig {
|
||||
timezone: "-05:00".to_string(),
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
assert_eq!(negative.general.timezone, "-05:00");
|
||||
|
||||
let zero = Config {
|
||||
general: GeneralConfig {
|
||||
timezone: "+00:00".to_string(),
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
assert_eq!(zero.general.timezone, "+00:00");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod credentials_tests {
|
||||
use super::*;
|
||||
use std::env;
|
||||
|
||||
/// Test: Загрузка credentials из переменных окружения
|
||||
#[test]
|
||||
fn test_load_credentials_from_env() {
|
||||
// Устанавливаем env переменные для теста
|
||||
env::set_var("API_ID", "12345");
|
||||
env::set_var("API_HASH", "test_hash_from_env");
|
||||
|
||||
// Загружаем credentials
|
||||
let result = Config::load_credentials();
|
||||
|
||||
// Проверяем что загрузилось из env
|
||||
// Примечание: этот тест может зафейлиться если есть credentials файл,
|
||||
// так как он имеет приоритет. Для полноценного тестирования нужно
|
||||
// моковать файловую систему или использовать временные директории.
|
||||
if result.is_ok() {
|
||||
let (api_id, api_hash) = result.unwrap();
|
||||
// Может быть либо из файла, либо из env
|
||||
assert!(api_id > 0);
|
||||
assert!(!api_hash.is_empty());
|
||||
}
|
||||
|
||||
// Очищаем env переменные после теста
|
||||
env::remove_var("API_ID");
|
||||
env::remove_var("API_HASH");
|
||||
}
|
||||
|
||||
/// Test: Проверка формата ошибки когда credentials не найдены
|
||||
#[test]
|
||||
fn test_load_credentials_error_message() {
|
||||
// Проверяем есть ли credentials файл в системе
|
||||
let has_credentials_file = Config::credentials_path()
|
||||
.map(|p| p.exists())
|
||||
.unwrap_or(false);
|
||||
|
||||
// Если есть credentials файл, тест не может проверить ошибку
|
||||
if has_credentials_file {
|
||||
// Просто проверяем что credentials загружаются
|
||||
let result = Config::load_credentials();
|
||||
assert!(result.is_ok(), "Credentials file exists but loading failed");
|
||||
return;
|
||||
}
|
||||
|
||||
// Временно сохраняем и удаляем env переменные
|
||||
let original_api_id = env::var("API_ID").ok();
|
||||
let original_api_hash = env::var("API_HASH").ok();
|
||||
|
||||
env::remove_var("API_ID");
|
||||
env::remove_var("API_HASH");
|
||||
|
||||
// Пытаемся загрузить credentials без файла и без env
|
||||
let result = Config::load_credentials();
|
||||
|
||||
// Должна быть ошибка
|
||||
if result.is_ok() {
|
||||
// Возможно env переменные установлены глобально и не удаляются
|
||||
// Тест пропускается
|
||||
eprintln!("Warning: credentials loaded despite removing env vars");
|
||||
} else {
|
||||
// Проверяем формат ошибки
|
||||
let err_msg = result.unwrap_err();
|
||||
assert!(!err_msg.is_empty(), "Error message should not be empty");
|
||||
}
|
||||
|
||||
// Восстанавливаем env переменные
|
||||
if let Some(api_id) = original_api_id {
|
||||
env::set_var("API_ID", api_id);
|
||||
}
|
||||
if let Some(api_hash) = original_api_hash {
|
||||
env::set_var("API_HASH", api_hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
175
tests/copy.rs
Normal file
175
tests/copy.rs
Normal file
@@ -0,0 +1,175 @@
|
||||
// Integration tests for copy message flow
|
||||
|
||||
mod helpers;
|
||||
|
||||
use helpers::test_data::TestMessageBuilder;
|
||||
|
||||
/// Test: Форматирование простого сообщения для копирования
|
||||
#[test]
|
||||
fn test_format_plain_message() {
|
||||
let msg = TestMessageBuilder::new("Hello, world!", 1)
|
||||
.sender("Alice")
|
||||
.outgoing()
|
||||
.build();
|
||||
|
||||
// Простое сообщение должно содержать только текст
|
||||
let formatted = format_message_for_test(&msg);
|
||||
assert_eq!(formatted, "Hello, world!");
|
||||
}
|
||||
|
||||
/// Test: Форматирование сообщения с forward контекстом
|
||||
#[test]
|
||||
fn test_format_message_with_forward() {
|
||||
let msg = TestMessageBuilder::new("Forwarded message", 1)
|
||||
.sender("Bob")
|
||||
.forwarded_from("Alice")
|
||||
.build();
|
||||
|
||||
// Сообщение с forward должно содержать контекст
|
||||
let formatted = format_message_for_test(&msg);
|
||||
assert!(formatted.contains("↪ Переслано от Alice"));
|
||||
assert!(formatted.contains("Forwarded message"));
|
||||
}
|
||||
|
||||
/// Test: Форматирование сообщения с reply контекстом
|
||||
#[test]
|
||||
fn test_format_message_with_reply() {
|
||||
let reply_msg = TestMessageBuilder::new("Reply text", 2)
|
||||
.sender("Bob")
|
||||
.reply_to(1, "Alice", "Original message")
|
||||
.build();
|
||||
|
||||
// Сообщение с reply должно содержать контекст оригинала
|
||||
let formatted = format_message_for_test(&reply_msg);
|
||||
assert!(formatted.contains("┌ Alice: Original message"));
|
||||
assert!(formatted.contains("Reply text"));
|
||||
}
|
||||
|
||||
/// Test: Форматирование сообщения с forward и reply одновременно
|
||||
#[test]
|
||||
fn test_format_message_with_both_contexts() {
|
||||
// Создаём сообщение с reply и forward
|
||||
let msg = TestMessageBuilder::new("Complex message", 2)
|
||||
.sender("Bob")
|
||||
.reply_to(1, "Alice", "Original")
|
||||
.forwarded_from("Charlie")
|
||||
.build();
|
||||
|
||||
let formatted = format_message_for_test(&msg);
|
||||
|
||||
// Должны быть оба контекста
|
||||
assert!(formatted.contains("↪ Переслано от Charlie"));
|
||||
assert!(formatted.contains("┌ Alice: Original"));
|
||||
assert!(formatted.contains("Complex message"));
|
||||
}
|
||||
|
||||
/// Test: Форматирование длинного сообщения
|
||||
#[test]
|
||||
fn test_format_long_message() {
|
||||
let long_text = "This is a very long message that spans multiple lines. ".repeat(10);
|
||||
let msg = TestMessageBuilder::new(&long_text, 1)
|
||||
.sender("Alice")
|
||||
.build();
|
||||
|
||||
let formatted = format_message_for_test(&msg);
|
||||
assert_eq!(formatted, long_text);
|
||||
}
|
||||
|
||||
/// Test: Форматирование сообщения с markdown entities
|
||||
#[test]
|
||||
fn test_format_message_with_markdown() {
|
||||
// Этот тест проверяет что entities сохраняются при копировании
|
||||
// В реальном коде entities конвертируются в markdown
|
||||
let msg = TestMessageBuilder::new("Bold text", 1)
|
||||
.sender("Alice")
|
||||
.build();
|
||||
|
||||
let formatted = format_message_for_test(&msg);
|
||||
// Для простоты проверяем что текст присутствует
|
||||
// В реальности здесь должна быть конвертация entities в markdown
|
||||
assert!(formatted.contains("Bold text"));
|
||||
}
|
||||
|
||||
// Helper функция для форматирования (упрощённая версия)
|
||||
// В реальном коде это делается в src/input/main_input.rs::format_message_for_clipboard
|
||||
fn format_message_for_test(msg: &tele_tui::tdlib::MessageInfo) -> String {
|
||||
let mut result = String::new();
|
||||
|
||||
// Добавляем forward контекст если есть
|
||||
if let Some(forward) = &msg.forward_from {
|
||||
result.push_str(&format!("↪ Переслано от {}\n", forward.sender_name));
|
||||
}
|
||||
|
||||
// Добавляем reply контекст если есть
|
||||
if let Some(reply) = &msg.reply_to {
|
||||
result.push_str(&format!("┌ {}: {}\n", reply.sender_name, reply.text));
|
||||
}
|
||||
|
||||
// Добавляем основной текст
|
||||
result.push_str(&msg.content);
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod clipboard_tests {
|
||||
use super::*;
|
||||
|
||||
/// Test: Проверка что clipboard функции не падают
|
||||
/// Примечание: Реальное тестирование clipboard требует GUI окружения
|
||||
/// и может быть ненадёжным в CI. Этот тест просто проверяет что
|
||||
/// arboard::Clipboard инициализируется без ошибок.
|
||||
#[test]
|
||||
#[ignore] // Игнорируем в CI, так как может не быть GUI окружения
|
||||
fn test_clipboard_initialization() {
|
||||
use arboard::Clipboard;
|
||||
|
||||
// Проверяем что можем создать clipboard
|
||||
let result = Clipboard::new();
|
||||
|
||||
// В headless окружении может вернуть ошибку - это нормально
|
||||
// Главное что не паникует
|
||||
match result {
|
||||
Ok(_) => {
|
||||
// Clipboard доступен - отлично!
|
||||
}
|
||||
Err(_) => {
|
||||
// Clipboard недоступен - ожидаемо в headless окружении
|
||||
// Тест всё равно проходит
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Test: Копирование в реальный clipboard (только для локального тестирования)
|
||||
#[test]
|
||||
#[ignore] // Игнорируем по умолчанию, запускать вручную: cargo test --ignored
|
||||
fn test_copy_to_real_clipboard() {
|
||||
use arboard::Clipboard;
|
||||
|
||||
let test_text = "Test message for clipboard";
|
||||
|
||||
// Пытаемся скопировать
|
||||
if let Ok(mut clipboard) = Clipboard::new() {
|
||||
let copy_result = clipboard.set_text(test_text);
|
||||
assert!(copy_result.is_ok(), "Failed to copy to clipboard");
|
||||
|
||||
// Пытаемся прочитать обратно
|
||||
if let Ok(content) = clipboard.get_text() {
|
||||
assert_eq!(content, test_text, "Clipboard content mismatch");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Test: Кроссплатформенность clipboard
|
||||
#[test]
|
||||
fn test_clipboard_availability() {
|
||||
use arboard::Clipboard;
|
||||
|
||||
// Этот тест просто проверяет что arboard доступен на всех платформах
|
||||
// arboard поддерживает: Linux (X11/Wayland), Windows, macOS
|
||||
let _clipboard_available = Clipboard::new().is_ok();
|
||||
|
||||
// Тест всегда проходит - мы просто проверяем что код компилируется
|
||||
// и не паникует на разных платформах
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user