Delete tests/bad-integration_tests.rs
This commit is contained in:
parent
54fb4e5f57
commit
68ae7c89cf
@ -1,753 +0,0 @@
|
||||
// tests/integration_tests.rs
|
||||
//! Интеграционный тестовый набор для Futriix Database
|
||||
//!
|
||||
//! Автономная утилита тестирования, поддерживающая различные типы тестов:
|
||||
//! - Регрессионные тесты
|
||||
//! - Unit-тесты
|
||||
//! - Smoke-тесты
|
||||
//! - Нагрузочные тесты
|
||||
//! - Стресс-тесты
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::time::{Duration, Instant};
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::thread;
|
||||
use std::process::Command;
|
||||
use std::env;
|
||||
use std::path::Path;
|
||||
|
||||
/// Типы поддерживаемых тестов
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum TestType {
|
||||
Regression, // Регрессионный тест
|
||||
Unit, // Unit-тест
|
||||
Smoke, // Smoke-тест
|
||||
Load, // Нагрузочный тест
|
||||
Stress, // Стресс-тест
|
||||
}
|
||||
|
||||
/// Результат выполнения теста
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TestResult {
|
||||
pub name: String,
|
||||
pub test_type: TestType,
|
||||
pub duration: Duration,
|
||||
pub passed: bool,
|
||||
pub error_message: Option<String>,
|
||||
pub metrics: HashMap<String, f64>,
|
||||
}
|
||||
|
||||
/// Тестовый случай
|
||||
pub struct TestCase {
|
||||
pub name: String,
|
||||
pub test_type: TestType,
|
||||
pub function: fn() -> Result<(), String>,
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
/// Реестр тестов
|
||||
pub struct TestRegistry {
|
||||
tests: Vec<TestCase>,
|
||||
results: Vec<TestResult>,
|
||||
}
|
||||
|
||||
impl TestRegistry {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
tests: Vec::new(),
|
||||
results: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Регистрация нового теста
|
||||
pub fn register_test(&mut self, name: &str, test_type: TestType, function: fn() -> Result<(), String>) {
|
||||
self.tests.push(TestCase {
|
||||
name: name.to_string(),
|
||||
test_type,
|
||||
function,
|
||||
enabled: true,
|
||||
});
|
||||
}
|
||||
|
||||
/// Запуск всех тестов
|
||||
pub fn run_all_tests(&mut self) -> &[TestResult] {
|
||||
self.results.clear();
|
||||
println!("Запуск всех тестов...");
|
||||
|
||||
// Создаем копию списка тестов для итерации, чтобы избежать проблем с заимствованием
|
||||
let test_names: Vec<String> = self.tests.iter().filter(|t| t.enabled).map(|t| t.name.clone()).collect();
|
||||
|
||||
for test_name in test_names {
|
||||
if let Some(test) = self.tests.iter().find(|t| t.name == test_name && t.enabled) {
|
||||
self.run_single_test(test);
|
||||
}
|
||||
}
|
||||
|
||||
&self.results
|
||||
}
|
||||
|
||||
/// Запуск тестов по фильтру имени
|
||||
pub fn run_tests_with_filter(&mut self, filter: &str) -> &[TestResult] {
|
||||
self.results.clear();
|
||||
println!("Запуск тестов с фильтром: '{}'", filter);
|
||||
|
||||
let test_names: Vec<String> = self.tests.iter()
|
||||
.filter(|t| t.enabled && t.name.contains(filter))
|
||||
.map(|t| t.name.clone())
|
||||
.collect();
|
||||
|
||||
for test_name in test_names {
|
||||
if let Some(test) = self.tests.iter().find(|t| t.name == test_name) {
|
||||
self.run_single_test(test);
|
||||
}
|
||||
}
|
||||
|
||||
&self.results
|
||||
}
|
||||
|
||||
/// Запуск конкретного теста по имени
|
||||
pub fn run_specific_test(&mut self, test_name: &str) -> Option<&TestResult> {
|
||||
self.results.clear();
|
||||
|
||||
if let Some(test) = self.tests.iter().find(|t| t.name == test_name && t.enabled) {
|
||||
self.run_single_test(test);
|
||||
return self.results.last();
|
||||
}
|
||||
|
||||
println!("Тест '{}' не найден или отключен", test_name);
|
||||
None
|
||||
}
|
||||
|
||||
/// Запуск тестов определенного типа
|
||||
pub fn run_tests_by_type(&mut self, test_type: TestType) -> &[TestResult] {
|
||||
self.results.clear();
|
||||
println!("Запуск тестов типа: {:?}", test_type);
|
||||
|
||||
let test_names: Vec<String> = self.tests.iter()
|
||||
.filter(|t| t.enabled && t.test_type == test_type)
|
||||
.map(|t| t.name.clone())
|
||||
.collect();
|
||||
|
||||
for test_name in test_names {
|
||||
if let Some(test) = self.tests.iter().find(|t| t.name == test_name) {
|
||||
self.run_single_test(test);
|
||||
}
|
||||
}
|
||||
|
||||
&self.results
|
||||
}
|
||||
|
||||
/// Выполнение одного теста
|
||||
fn run_single_test(&mut self, test: &TestCase) {
|
||||
print!("Запуск теста '{}' ({:?})... ", test.name, test.test_type);
|
||||
|
||||
let start_time = Instant::now();
|
||||
let result = (test.function)();
|
||||
let duration = start_time.elapsed();
|
||||
|
||||
match result {
|
||||
Ok(()) => {
|
||||
println!("✅ УСПЕХ ({:?})", duration);
|
||||
self.results.push(TestResult {
|
||||
name: test.name.clone(),
|
||||
test_type: test.test_type.clone(),
|
||||
duration,
|
||||
passed: true,
|
||||
error_message: None,
|
||||
metrics: HashMap::new(),
|
||||
});
|
||||
}
|
||||
Err(error) => {
|
||||
println!("❌ ОШИБКА ({:?})", duration);
|
||||
println!(" Причина: {}", error);
|
||||
self.results.push(TestResult {
|
||||
name: test.name.clone(),
|
||||
test_type: test.test_type.clone(),
|
||||
duration,
|
||||
passed: false,
|
||||
error_message: Some(error),
|
||||
metrics: HashMap::new(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Статистика результатов
|
||||
pub fn print_statistics(&self) {
|
||||
let total = self.results.len();
|
||||
let passed = self.results.iter().filter(|r| r.passed).count();
|
||||
let failed = total - passed;
|
||||
|
||||
println!("\n📊 СТАТИСТИКА ТЕСТИРОВАНИЯ:");
|
||||
println!("Всего тестов: {}", total);
|
||||
println!("Успешных: {} ✅", passed);
|
||||
println!("Проваленных: {} ❌", failed);
|
||||
|
||||
if !self.results.is_empty() {
|
||||
let total_duration: Duration = self.results.iter().map(|r| r.duration).sum();
|
||||
println!("Общее время выполнения: {:?}", total_duration);
|
||||
|
||||
// Группировка по типам тестов
|
||||
let mut type_stats: HashMap<TestType, (usize, usize)> = HashMap::new();
|
||||
for result in &self.results {
|
||||
let entry = type_stats.entry(result.test_type.clone()).or_insert((0, 0));
|
||||
if result.passed {
|
||||
entry.0 += 1;
|
||||
} else {
|
||||
entry.1 += 1;
|
||||
}
|
||||
}
|
||||
|
||||
println!("\n📈 Статистика по типам тестов:");
|
||||
for (test_type, (passed_count, failed_count)) in type_stats {
|
||||
let total_type = passed_count + failed_count;
|
||||
println!(" {:?}: {}/{} успешных", test_type, passed_count, total_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== РЕГРЕССИОННЫЕ ТЕСТЫ ====================
|
||||
|
||||
/// Тест базовой функциональности CRUD операций
|
||||
fn regression_test_basic_crud() -> Result<(), String> {
|
||||
// Здесь должна быть реальная логика тестирования Futriix
|
||||
// Для демонстрации используем заглушки
|
||||
|
||||
// Имитация создания документа
|
||||
thread::sleep(Duration::from_millis(50));
|
||||
|
||||
// Имитация чтения документа
|
||||
thread::sleep(Duration::from_millis(30));
|
||||
|
||||
// Имитация обновления документа
|
||||
thread::sleep(Duration::from_millis(40));
|
||||
|
||||
// Имитация удаления документа
|
||||
thread::sleep(Duration::from_millis(20));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Тест работы с индексами
|
||||
fn regression_test_index_operations() -> Result<(), String> {
|
||||
// Имитация создания индекса
|
||||
thread::sleep(Duration::from_millis(80));
|
||||
|
||||
// Имитация запроса по индексу
|
||||
thread::sleep(Duration::from_millis(60));
|
||||
|
||||
// Имитация перестроения индекса
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Тест транзакций
|
||||
fn regression_test_transactions() -> Result<(), String> {
|
||||
// Имитация начала транзакции
|
||||
thread::sleep(Duration::from_millis(20));
|
||||
|
||||
// Имитация операций в транзакции
|
||||
thread::sleep(Duration::from_millis(70));
|
||||
|
||||
// Имитация коммита транзакции
|
||||
thread::sleep(Duration::from_millis(30));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// ==================== UNIT-ТЕСТЫ ====================
|
||||
|
||||
/// Тест модуля индексов
|
||||
fn unit_test_index_module() -> Result<(), String> {
|
||||
// Имитация тестирования создания B-дерева
|
||||
thread::sleep(Duration::from_millis(40));
|
||||
|
||||
// Имитация тестирования поиска по индексу
|
||||
thread::sleep(Duration::from_millis(35));
|
||||
|
||||
// Имитация тестирования удаления из индекса
|
||||
thread::sleep(Duration::from_millis(25));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Тест модуля шардинга
|
||||
fn unit_test_sharding_module() -> Result<(), String> {
|
||||
// Имитация тестирования консистентного хэширования
|
||||
thread::sleep(Duration::from_millis(60));
|
||||
|
||||
// Имитация тестирования распределения данных
|
||||
thread::sleep(Duration::from_millis(55));
|
||||
|
||||
// Имитация тестирования миграции шардов
|
||||
thread::sleep(Duration::from_millis(75));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Тест модуля репликации
|
||||
fn unit_test_replication_module() -> Result<(), String> {
|
||||
// Имитация тестирования Raft протокола
|
||||
thread::sleep(Duration::from_millis(90));
|
||||
|
||||
// Имитация тестирования синхронизации данных
|
||||
thread::sleep(Duration::from_millis(65));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// ==================== SMOKE-ТЕСТЫ ====================
|
||||
|
||||
/// Базовый smoke-тест запуска системы
|
||||
fn smoke_test_system_startup() -> Result<(), String> {
|
||||
// Проверка доступности бинарного файла Futriix
|
||||
if !Path::new("./target/debug/futriix").exists() && !Path::new("./futriix").exists() {
|
||||
return Err("Бинарный файл Futriix не найден".to_string());
|
||||
}
|
||||
|
||||
// Имитация проверки конфигурации
|
||||
thread::sleep(Duration::from_millis(30));
|
||||
|
||||
// Имитация инициализации базы данных
|
||||
thread::sleep(Duration::from_millis(50));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Smoke-тест API
|
||||
fn smoke_test_api_access() -> Result<(), String> {
|
||||
// Имитация проверки HTTP API
|
||||
thread::sleep(Duration::from_millis(40));
|
||||
|
||||
// Имитация проверки Lua API
|
||||
thread::sleep(Duration::from_millis(35));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Smoke-тест кластера
|
||||
fn smoke_test_cluster_formation() -> Result<(), String> {
|
||||
// Имитация проверки формирования кластера
|
||||
thread::sleep(Duration::from_millis(70));
|
||||
|
||||
// Имитация проверки выбора лидера
|
||||
thread::sleep(Duration::from_millis(60));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// ==================== НАГРУЗОЧНЫЕ ТЕСТЫ ====================
|
||||
|
||||
/// Нагрузочный тест записи
|
||||
fn load_test_write_operations() -> Result<(), String> {
|
||||
println!("\n 🚀 Запуск нагрузочного теста записи...");
|
||||
|
||||
const OPERATIONS: usize = 1000;
|
||||
let start_time = Instant::now();
|
||||
|
||||
// Используем Arc для безопасного разделения счетчика между потоками
|
||||
use std::sync::Arc;
|
||||
let counter = Arc::new(AtomicUsize::new(0));
|
||||
|
||||
// Имитация параллельных операций записи
|
||||
let handles: Vec<_> = (0..4).map(|_| {
|
||||
let counter = Arc::clone(&counter);
|
||||
thread::spawn(move || {
|
||||
for _ in 0..OPERATIONS/4 {
|
||||
// Имитация операции записи
|
||||
thread::sleep(Duration::from_micros(500));
|
||||
counter.fetch_add(1, Ordering::SeqCst);
|
||||
}
|
||||
})
|
||||
}).collect();
|
||||
|
||||
for handle in handles {
|
||||
handle.join().map_err(|e| format!("Ошибка потока: {:?}", e))?;
|
||||
}
|
||||
|
||||
let duration = start_time.elapsed();
|
||||
let ops_per_sec = OPERATIONS as f64 / duration.as_secs_f64();
|
||||
|
||||
println!(" ✅ Завершено {} операций за {:?} ({:.2} оп/сек)",
|
||||
OPERATIONS, duration, ops_per_sec);
|
||||
|
||||
if ops_per_sec < 1000.0 {
|
||||
return Err(format!("Низкая производительность: {:.2} оп/сек", ops_per_sec));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Нагрузочный тест чтения
|
||||
fn load_test_read_operations() -> Result<(), String> {
|
||||
println!("\n 📖 Запуск нагрузочного теста чтения...");
|
||||
|
||||
const OPERATIONS: usize = 2000;
|
||||
let start_time = Instant::now();
|
||||
|
||||
// Имитация параллельных операций чтения
|
||||
let handles: Vec<_> = (0..8).map(|_| {
|
||||
thread::spawn(move || {
|
||||
for _ in 0..OPERATIONS/8 {
|
||||
// Имитация операции чтения
|
||||
thread::sleep(Duration::from_micros(200));
|
||||
}
|
||||
})
|
||||
}).collect();
|
||||
|
||||
for handle in handles {
|
||||
handle.join().map_err(|e| format!("Ошибка потока: {:?}", e))?;
|
||||
}
|
||||
|
||||
let duration = start_time.elapsed();
|
||||
let ops_per_sec = OPERATIONS as f64 / duration.as_secs_f64();
|
||||
|
||||
println!(" ✅ Завершено {} операций за {:?} ({:.2} оп/сек)",
|
||||
OPERATIONS, duration, ops_per_sec);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Нагрузочный тест смешанных операций
|
||||
fn load_test_mixed_operations() -> Result<(), String> {
|
||||
println!("\n 🔄 Запуск нагрузочного теста смешанных операций...");
|
||||
|
||||
const OPERATIONS: usize = 1500;
|
||||
let start_time = Instant::now();
|
||||
|
||||
let write_handles: Vec<_> = (0..2).map(|_| {
|
||||
thread::spawn(move || {
|
||||
for _ in 0..OPERATIONS/4 {
|
||||
thread::sleep(Duration::from_micros(600));
|
||||
}
|
||||
})
|
||||
}).collect();
|
||||
|
||||
let read_handles: Vec<_> = (0..4).map(|_| {
|
||||
thread::spawn(move || {
|
||||
for _ in 0..OPERATIONS/4 {
|
||||
thread::sleep(Duration::from_micros(300));
|
||||
}
|
||||
})
|
||||
}).collect();
|
||||
|
||||
for handle in write_handles {
|
||||
handle.join().map_err(|e| format!("Ошибка потока записи: {:?}", e))?;
|
||||
}
|
||||
|
||||
for handle in read_handles {
|
||||
handle.join().map_err(|e| format!("Ошибка потока чтения: {:?}", e))?;
|
||||
}
|
||||
|
||||
let duration = start_time.elapsed();
|
||||
let ops_per_sec = OPERATIONS as f64 / duration.as_secs_f64();
|
||||
|
||||
println!(" ✅ Завершено {} операций за {:?} ({:.2} оп/сек)",
|
||||
OPERATIONS, duration, ops_per_sec);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// ==================== СТРЕСС-ТЕСТЫ ====================
|
||||
|
||||
/// Стресс-тест памяти
|
||||
fn stress_test_memory_usage() -> Result<(), String> {
|
||||
println!("\n 💾 Запуск стресс-теста памяти...");
|
||||
|
||||
const ITERATIONS: usize = 50;
|
||||
|
||||
for i in 0..ITERATIONS {
|
||||
// Имитация интенсивного использования памяти
|
||||
let mut data = Vec::with_capacity(10000);
|
||||
for j in 0..10000 {
|
||||
data.push(format!("stress_data_{}_{}", i, j));
|
||||
}
|
||||
|
||||
// Имитация обработки данных
|
||||
thread::sleep(Duration::from_millis(10));
|
||||
|
||||
if i % 10 == 0 {
|
||||
println!(" ... итерация {}/{}", i, ITERATIONS);
|
||||
}
|
||||
|
||||
// Для демонстрации используем простую логику вместо rand
|
||||
// Симуляция случайного сбоя при высокой нагрузке
|
||||
if i > 30 && i % 7 == 0 { // Детерминированная замена rand::random
|
||||
return Err("Обнаружена утечка памяти при высокой нагрузке".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
println!(" ✅ Стресс-тест памяти завершен успешно");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Стресс-тест подключений
|
||||
fn stress_test_connections() -> Result<(), String> {
|
||||
println!("\n 🔌 Запуск стресс-теста подключений...");
|
||||
|
||||
const CONNECTIONS: usize = 100;
|
||||
|
||||
let handles: Vec<_> = (0..CONNECTIONS).map(|i| {
|
||||
thread::spawn(move || {
|
||||
// Имитация установки соединения
|
||||
thread::sleep(Duration::from_millis(5));
|
||||
|
||||
// Имитация работы соединения
|
||||
for j in 0..10 {
|
||||
thread::sleep(Duration::from_millis(2));
|
||||
|
||||
// Детерминированная замена rand::random
|
||||
// Симуляция случайного разрыва соединения
|
||||
if (i + j) % 67 == 0 { // Простая детерминированная логика
|
||||
return Err(format!("Соединение {} разорвано на операции {}", i, j));
|
||||
}
|
||||
}
|
||||
|
||||
// Имитация закрытия соединения
|
||||
thread::sleep(Duration::from_millis(3));
|
||||
Ok(())
|
||||
})
|
||||
}).collect();
|
||||
|
||||
let mut errors = Vec::new();
|
||||
for (i, handle) in handles.into_iter().enumerate() {
|
||||
match handle.join() {
|
||||
Ok(Ok(())) => {},
|
||||
Ok(Err(e)) => errors.push(format!("Соединение {}: {}", i, e)),
|
||||
Err(e) => errors.push(format!("Поток {} упал: {:?}", i, e)),
|
||||
}
|
||||
}
|
||||
|
||||
if errors.len() > CONNECTIONS / 10 {
|
||||
return Err(format!("Слишком много ошибок соединений: {}/{}", errors.len(), CONNECTIONS));
|
||||
}
|
||||
|
||||
println!(" ✅ Стресс-тест подключений завершен (ошибок: {})", errors.len());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Стресс-тест отказоустойчивости
|
||||
fn stress_test_fault_tolerance() -> Result<(), String> {
|
||||
println!("\n 🛡️ Запуск стресс-теста отказоустойчивости...");
|
||||
|
||||
// Имитация различных сбоев
|
||||
let failure_scenarios = vec![
|
||||
"network_partition",
|
||||
"node_failure",
|
||||
"disk_full",
|
||||
"memory_pressure"
|
||||
];
|
||||
|
||||
for scenario in failure_scenarios {
|
||||
println!(" ... тестирование сценария: {}", scenario);
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
|
||||
// Имитация восстановления после сбоя
|
||||
thread::sleep(Duration::from_millis(50));
|
||||
|
||||
// Детерминированная замена rand::random для демонстрации
|
||||
// 5% вероятность сбоя теста для демонстрации
|
||||
if scenario == "disk_full" { // Детерминированный сбой для демонстрации
|
||||
return Err(format!("Система не восстановилась после сценария: {}", scenario));
|
||||
}
|
||||
}
|
||||
|
||||
println!(" ✅ Все сценарии отказоустойчивости пройдены");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// ==================== ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ ====================
|
||||
|
||||
/// Инициализация реестра тестов
|
||||
fn initialize_test_registry() -> TestRegistry {
|
||||
let mut registry = TestRegistry::new();
|
||||
|
||||
// Регистрация регрессионных тестов
|
||||
registry.register_test("basic_crud", TestType::Regression, regression_test_basic_crud);
|
||||
registry.register_test("index_operations", TestType::Regression, regression_test_index_operations);
|
||||
registry.register_test("transactions", TestType::Regression, regression_test_transactions);
|
||||
|
||||
// Регистрация unit-тестов
|
||||
registry.register_test("index_module", TestType::Unit, unit_test_index_module);
|
||||
registry.register_test("sharding_module", TestType::Unit, unit_test_sharding_module);
|
||||
registry.register_test("replication_module", TestType::Unit, unit_test_replication_module);
|
||||
|
||||
// Регистрация smoke-тестов
|
||||
registry.register_test("system_startup", TestType::Smoke, smoke_test_system_startup);
|
||||
registry.register_test("api_access", TestType::Smoke, smoke_test_api_access);
|
||||
registry.register_test("cluster_formation", TestType::Smoke, smoke_test_cluster_formation);
|
||||
|
||||
// Регистрация нагрузочных тестов
|
||||
registry.register_test("write_operations", TestType::Load, load_test_write_operations);
|
||||
registry.register_test("read_operations", TestType::Load, load_test_read_operations);
|
||||
registry.register_test("mixed_operations", TestType::Load, load_test_mixed_operations);
|
||||
|
||||
// Регистрация стресс-тестов
|
||||
registry.register_test("memory_usage", TestType::Stress, stress_test_memory_usage);
|
||||
registry.register_test("connections", TestType::Stress, stress_test_connections);
|
||||
registry.register_test("fault_tolerance", TestType::Stress, stress_test_fault_tolerance);
|
||||
|
||||
registry
|
||||
}
|
||||
|
||||
/// Вывод справки по использованию
|
||||
fn print_usage() {
|
||||
println!();
|
||||
println!("🎯 ИНТЕГРАЦИОННЫЙ ТЕСТОВЫЙ НАБОР ДЛЯ FUTRIX");
|
||||
println!();
|
||||
println!("Использование:");
|
||||
println!(" cargo test integration_tests # Запуск всех тестов");
|
||||
println!(" cargo test integration_tests -- <фильтр> # Запуск тестов по фильтру");
|
||||
println!(" cargo test integration_tests -- <имя_теста> # Запуск конкретного теста");
|
||||
println!(" cargo test integration_tests -- --types # Запуск по типам тестов");
|
||||
println!(" cargo test integration_tests -- --help # Показать эту справку");
|
||||
println!();
|
||||
println!("Доступные типы тестов:");
|
||||
println!(" --regression Регрессионные тесты");
|
||||
println!(" --unit Unit-тесты");
|
||||
println!(" --smoke Smoke-тесты");
|
||||
println!(" --load Нагрузочные тесты");
|
||||
println!(" --stress Стресс-тесты");
|
||||
println!();
|
||||
println!("Примеры:");
|
||||
println!(" cargo test integration_tests -- basic # Тесты с 'basic' в названии");
|
||||
println!(" cargo test integration_tests -- --load # Все нагрузочные тесты");
|
||||
println!(" cargo test integration_tests -- api_access # Конкретный тест");
|
||||
}
|
||||
|
||||
/// Основная функция (для запуска как бинарника)
|
||||
fn main() {
|
||||
println!("🚀 Запуск интеграционного тестового набора Futriix...");
|
||||
|
||||
let args: Vec<String> = env::args().skip(1).collect();
|
||||
|
||||
let mut registry = initialize_test_registry();
|
||||
|
||||
if args.is_empty() {
|
||||
// Запуск всех тестов
|
||||
registry.run_all_tests();
|
||||
} else {
|
||||
match args[0].as_str() {
|
||||
"--help" | "-h" => {
|
||||
print_usage();
|
||||
return;
|
||||
}
|
||||
"--types" | "-t" => {
|
||||
if args.len() > 1 {
|
||||
match args[1].as_str() {
|
||||
"--regression" | "regression" => {
|
||||
registry.run_tests_by_type(TestType::Regression);
|
||||
}
|
||||
"--unit" | "unit" => {
|
||||
registry.run_tests_by_type(TestType::Unit);
|
||||
}
|
||||
"--smoke" | "smoke" => {
|
||||
registry.run_tests_by_type(TestType::Smoke);
|
||||
}
|
||||
"--load" | "load" => {
|
||||
registry.run_tests_by_type(TestType::Load);
|
||||
}
|
||||
"--stress" | "stress" => {
|
||||
registry.run_tests_by_type(TestType::Stress);
|
||||
}
|
||||
_ => {
|
||||
println!("Неизвестный тип тестов: {}", args[1]);
|
||||
print_usage();
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println!("Укажите тип тестов после --types");
|
||||
print_usage();
|
||||
return;
|
||||
}
|
||||
}
|
||||
"--regression" => {
|
||||
registry.run_tests_by_type(TestType::Regression);
|
||||
}
|
||||
"--unit" => {
|
||||
registry.run_tests_by_type(TestType::Unit);
|
||||
}
|
||||
"--smoke" => {
|
||||
registry.run_tests_by_type(TestType::Smoke);
|
||||
}
|
||||
"--load" => {
|
||||
registry.run_tests_by_type(TestType::Load);
|
||||
}
|
||||
"--stress" => {
|
||||
registry.run_tests_by_type(TestType::Stress);
|
||||
}
|
||||
filter => {
|
||||
if filter.starts_with("--") {
|
||||
println!("Неизвестная опция: {}", filter);
|
||||
print_usage();
|
||||
return;
|
||||
}
|
||||
|
||||
// Попробуем найти точное совпадение имени теста
|
||||
if let Some(result) = registry.run_specific_test(filter) {
|
||||
if !result.passed {
|
||||
std::process::exit(1);
|
||||
}
|
||||
} else {
|
||||
// Если точного совпадения нет, запустим по фильтру
|
||||
registry.run_tests_with_filter(filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Вывод статистики
|
||||
registry.print_statistics();
|
||||
|
||||
// Проверка наличия проваленных тестов
|
||||
let has_failures = registry.results.iter().any(|r| !r.passed);
|
||||
if has_failures {
|
||||
println!("\n❌ Обнаружены проваленные тесты!");
|
||||
std::process::exit(1);
|
||||
} else if !registry.results.is_empty() {
|
||||
println!("\n✅ Все тесты пройдены успешно!");
|
||||
}
|
||||
}
|
||||
|
||||
/// Модуль с тестами для самой тестовой системы
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_registry_initialization() {
|
||||
let registry = initialize_test_registry();
|
||||
assert!(!registry.tests.is_empty(), "Реестр тестов должен быть не пустым");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_test_types() {
|
||||
assert_eq!(TestType::Regression, TestType::Regression);
|
||||
assert_ne!(TestType::Regression, TestType::Unit);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_successful_test() {
|
||||
let success_fn = || Ok(());
|
||||
let mut registry = TestRegistry::new();
|
||||
registry.register_test("success_test", TestType::Regression, success_fn);
|
||||
|
||||
let result = registry.run_specific_test("success_test").unwrap();
|
||||
assert!(result.passed);
|
||||
assert!(result.error_message.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_failed_test() {
|
||||
let fail_fn = || Err("Intentional failure".to_string());
|
||||
let mut registry = TestRegistry::new();
|
||||
registry.register_test("fail_test", TestType::Regression, fail_fn);
|
||||
|
||||
let result = registry.run_specific_test("fail_test").unwrap();
|
||||
assert!(!result.passed);
|
||||
assert!(result.error_message.is_some());
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user