提交qiming-mcp-proxy

This commit is contained in:
Codex
2026-06-01 13:03:20 +08:00
parent 9e9486b7c2
commit afb3d9f4e6
394 changed files with 124494 additions and 0 deletions

View File

@@ -0,0 +1,243 @@
use document_parser::utils::environment_manager::{
EnvironmentManager, InstallProgress, IssueSeverity, RetryConfig,
};
use std::time::Duration;
use tempfile::TempDir;
use tokio::sync::mpsc;
#[tokio::test]
async fn test_environment_manager_with_retry_config() {
let temp_dir = TempDir::new().unwrap();
let retry_config = RetryConfig {
max_attempts: 2,
base_delay: Duration::from_millis(100),
max_delay: Duration::from_secs(5),
backoff_multiplier: 2.0,
};
let manager = EnvironmentManager::new(
"python3".to_string(),
temp_dir.path().to_string_lossy().to_string(),
)
.with_retry_config(retry_config)
.with_timeout(Duration::from_secs(30))
.with_cache_ttl(Duration::from_secs(60));
// 测试环境检查
let result = manager.check_environment().await;
assert!(result.is_ok());
let status = result.unwrap();
assert!(status.health_score() <= 100);
// 测试缓存功能
let cached_result = manager.check_environment().await;
assert!(cached_result.is_ok());
}
#[tokio::test]
async fn test_environment_manager_with_progress_tracking() {
let temp_dir = TempDir::new().unwrap();
let (tx, mut rx) = mpsc::unbounded_channel::<InstallProgress>();
let manager = EnvironmentManager::with_progress_tracking(
"python3".to_string(),
temp_dir.path().to_string_lossy().to_string(),
tx,
);
// 在后台任务中监听进度
let progress_task = tokio::spawn(async move {
let mut progress_count = 0;
while let Some(progress) = rx.recv().await {
progress_count += 1;
println!(
"Progress: {} - {} ({}%)",
progress.package, progress.message, progress.progress
);
// 避免无限等待
if progress_count > 10 {
break;
}
}
progress_count
});
// 执行环境检查
let result = manager.check_environment().await;
assert!(result.is_ok());
// 等待进度任务完成(带超时)
let progress_result = tokio::time::timeout(Duration::from_secs(5), progress_task).await;
// 验证进度跟踪是否工作
if let Ok(Ok(count)) = progress_result {
println!("Received {count} progress updates");
}
}
#[tokio::test]
async fn test_environment_status_analysis() {
let temp_dir = TempDir::new().unwrap();
let manager = EnvironmentManager::new(
"python3".to_string(),
temp_dir.path().to_string_lossy().to_string(),
);
let status = manager.check_environment().await.unwrap();
// 测试状态分析方法
println!("Environment ready: {}", status.is_ready());
println!("Health score: {}/100", status.health_score());
println!("Has CUDA support: {}", status.has_cuda_support());
// 测试问题分析
let critical_issues = status.get_critical_issues();
let auto_fixable_issues = status.get_auto_fixable_issues();
println!("Critical issues: {}", critical_issues.len());
println!("Auto-fixable issues: {}", auto_fixable_issues.len());
for issue in critical_issues {
assert_eq!(issue.severity, IssueSeverity::Critical);
println!("Critical issue: {} - {}", issue.component, issue.message);
}
for issue in auto_fixable_issues {
assert!(issue.auto_fixable);
println!(
"Auto-fixable issue: {} - {}",
issue.component, issue.suggestion
);
}
}
#[tokio::test]
async fn test_environment_reporting() {
let temp_dir = TempDir::new().unwrap();
let manager = EnvironmentManager::new(
"python3".to_string(),
temp_dir.path().to_string_lossy().to_string(),
);
// 测试环境报告生成
let report = manager.generate_environment_report().await;
assert!(report.is_ok());
let report_content = report.unwrap();
assert!(report_content.contains("=== 环境检查报告 ==="));
assert!(report_content.contains("=== 组件状态 ==="));
println!("Environment Report:\n{report_content}");
// 测试环境摘要
let summary = manager.get_environment_summary().await;
assert!(summary.is_ok());
let summary_content = summary.unwrap();
assert!(summary_content.contains("环境状态:"));
assert!(summary_content.contains("健康评分:"));
println!("Environment Summary: {summary_content}");
}
#[tokio::test]
async fn test_environment_validation() {
let temp_dir = TempDir::new().unwrap();
let manager = EnvironmentManager::new(
"python3".to_string(),
temp_dir.path().to_string_lossy().to_string(),
);
// 测试环境验证
let is_valid = manager.validate_environment().await;
assert!(is_valid.is_ok());
let validation_result = is_valid.unwrap();
println!("Environment validation result: {validation_result}");
// 测试引擎验证
let engines_valid = manager.validate_engines().await;
assert!(engines_valid.is_ok());
let engines_result = engines_valid.unwrap();
println!("Engines validation result: {engines_result}");
}
#[tokio::test]
async fn test_cache_functionality() {
let temp_dir = TempDir::new().unwrap();
let manager = EnvironmentManager::new(
"python3".to_string(),
temp_dir.path().to_string_lossy().to_string(),
)
.with_cache_ttl(Duration::from_secs(1)); // 短缓存时间用于测试
// 第一次检查
let start_time = std::time::Instant::now();
let result1 = manager.check_environment().await.unwrap();
let first_check_duration = start_time.elapsed();
// 立即第二次检查(应该使用缓存)
let start_time = std::time::Instant::now();
let result2 = manager.check_environment().await.unwrap();
let second_check_duration = start_time.elapsed();
// 缓存的检查应该更快,但由于测试环境的不确定性,我们只检查结果一致性
// assert!(second_check_duration < first_check_duration);
// 验证结果一致性
assert_eq!(result1.python_available, result2.python_available);
assert_eq!(result1.uv_available, result2.uv_available);
// 等待缓存过期
tokio::time::sleep(Duration::from_secs(2)).await;
// 清除缓存
manager.clear_cache().await;
// 第三次检查(缓存已过期)
let start_time = std::time::Instant::now();
let result3 = manager.check_environment().await.unwrap();
let third_check_duration = start_time.elapsed();
// 过期后的检查可能比缓存检查慢,但在测试环境中可能不稳定
// 我们只验证缓存功能正常工作,不强制要求时间差异
println!("Cache functionality test completed - timing may vary in test environment");
println!("First check: {first_check_duration:?}");
println!("Second check (cached): {second_check_duration:?}");
println!("Third check (expired): {third_check_duration:?}");
}
#[tokio::test]
async fn test_concurrent_environment_checks() {
let temp_dir = TempDir::new().unwrap();
let manager = std::sync::Arc::new(EnvironmentManager::new(
"python3".to_string(),
temp_dir.path().to_string_lossy().to_string(),
));
// 并发执行多个环境检查
let mut handles = Vec::new();
for i in 0..5 {
let manager_clone = manager.clone();
let handle = tokio::spawn(async move {
let result = manager_clone.check_environment().await;
println!("Concurrent check {i} completed");
result
});
handles.push(handle);
}
// 等待所有检查完成
let results = futures::future::join_all(handles).await;
// 验证所有检查都成功
for (i, result) in results.into_iter().enumerate() {
assert!(result.is_ok(), "Concurrent check {i} failed");
let status = result.unwrap().unwrap();
assert!(status.health_score() <= 100);
}
}

View File

@@ -0,0 +1,312 @@
use document_parser::services::image_processor::{
ImageProcessor, ImageProcessorConfig, ImageUploadResult,
};
use std::path::Path;
use tempfile::TempDir;
#[tokio::test]
async fn test_image_processing_pipeline() {
let temp_dir = TempDir::new().unwrap();
let config = ImageProcessorConfig::default();
let processor = ImageProcessor::new(config, None);
// 创建测试图片文件
let test_images = create_test_images(&temp_dir).await;
// 测试批量处理
let result = processor.batch_upload_images(test_images.clone()).await;
assert!(result.is_ok());
let batch_result = result.unwrap();
assert_eq!(batch_result.len(), test_images.len());
// 检查结果
for upload_result in &batch_result {
// 由于没有OSS服务预期会失败但不会panic
assert!(!upload_result.success);
assert!(upload_result.error_message.is_some());
}
}
#[tokio::test]
async fn test_image_extraction_from_directory() {
let temp_dir = TempDir::new().unwrap();
let config = ImageProcessorConfig::default();
let processor = ImageProcessor::new(config, None);
// 创建测试目录结构
let test_dir = temp_dir.path().join("images");
tokio::fs::create_dir_all(&test_dir).await.unwrap();
// 创建测试图片
create_test_image(&test_dir.join("test1.jpg")).await;
create_test_image(&test_dir.join("test2.png")).await;
// 测试提取(注意:当前实现不支持递归子目录)
let result = processor
.extract_images_from_directory(test_dir.to_str().unwrap())
.await;
assert!(result.is_ok());
let image_paths = result.unwrap();
assert_eq!(image_paths.len(), 2);
}
#[tokio::test]
async fn test_error_handling_and_recovery() {
let temp_dir = TempDir::new().unwrap();
let config = ImageProcessorConfig::default();
let processor = ImageProcessor::new(config, None);
// 测试不存在的文件
let non_existent_files = vec![
"/non/existent/file1.jpg".to_string(),
"/non/existent/file2.png".to_string(),
];
let result = processor.batch_upload_images(non_existent_files).await;
assert!(result.is_ok());
let batch_result = result.unwrap();
assert_eq!(batch_result.len(), 2);
// 所有结果都应该失败
for upload_result in &batch_result {
assert!(!upload_result.success);
assert!(upload_result.error_message.is_some());
}
}
#[tokio::test]
async fn test_performance_with_large_batch() {
let temp_dir = TempDir::new().unwrap();
let config = ImageProcessorConfig::default();
let processor = ImageProcessor::new(config, None);
// 创建大量测试图片
let mut test_images = Vec::new();
for i in 0..50 {
let image_path = temp_dir.path().join(format!("test_{i}.jpg"));
create_test_image(&image_path).await;
test_images.push(image_path.to_string_lossy().to_string());
}
let start_time = std::time::Instant::now();
let result = processor.batch_upload_images(test_images.clone()).await;
let processing_time = start_time.elapsed();
assert!(result.is_ok());
let batch_result = result.unwrap();
assert_eq!(batch_result.len(), test_images.len());
// 性能检查处理50个图片应该在合理时间内完成
assert!(
processing_time.as_secs() < 30,
"Processing took too long: {processing_time:?}"
);
println!(
"Processed {} images in {:?}",
test_images.len(),
processing_time
);
}
#[tokio::test]
async fn test_concurrent_processing() {
let temp_dir = TempDir::new().unwrap();
// 创建多个处理器实例
let config1 = ImageProcessorConfig::default();
let config2 = ImageProcessorConfig::default();
let processor1 = ImageProcessor::new(config1, None);
let processor2 = ImageProcessor::new(config2, None);
// 创建测试图片
let test_images1 = create_test_images(&temp_dir).await;
let test_images2 = create_test_images(&temp_dir).await;
// 并发处理
let (result1, result2): (
anyhow::Result<Vec<ImageUploadResult>>,
anyhow::Result<Vec<ImageUploadResult>>,
) = tokio::join!(
processor1.batch_upload_images(test_images1.clone()),
processor2.batch_upload_images(test_images2.clone())
);
assert!(result1.is_ok());
assert!(result2.is_ok());
let batch_result1 = result1.unwrap();
let batch_result2 = result2.unwrap();
assert_eq!(batch_result1.len(), test_images1.len());
assert_eq!(batch_result2.len(), test_images2.len());
}
#[tokio::test]
async fn test_markdown_image_replacement() {
let temp_dir = TempDir::new().unwrap();
let config = ImageProcessorConfig::default();
let processor = ImageProcessor::new(config, None);
// 创建测试图片
let image_path = temp_dir.path().join("test.jpg");
create_test_image(&image_path).await;
// 创建包含图片的Markdown内容
let markdown_content = format!(
"# Test Document\n\n![Test Image]({})\n\nSome text here.",
image_path.to_string_lossy()
);
// 测试替换由于没有OSS服务图片路径不会被替换
let result = processor.replace_markdown_images(&markdown_content).await;
assert!(result.is_ok());
let processed_content = result.unwrap();
// 由于没有OSS服务内容应该保持不变
assert_eq!(processed_content, markdown_content);
}
#[tokio::test]
async fn test_image_validation() {
let temp_dir = TempDir::new().unwrap();
let config = ImageProcessorConfig::default();
let processor = ImageProcessor::new(config, None);
// 创建有效的图片文件
let valid_image = temp_dir.path().join("valid.jpg");
create_test_image(&valid_image).await;
// 创建无效的文件(非图片格式)
let invalid_file = temp_dir.path().join("invalid.txt");
tokio::fs::write(&invalid_file, "not an image")
.await
.unwrap();
// 测试验证
let valid_result = processor
.validate_image_file(valid_image.to_str().unwrap())
.await;
assert!(valid_result.is_ok());
assert!(valid_result.unwrap());
let invalid_result = processor
.validate_image_file(invalid_file.to_str().unwrap())
.await;
assert!(invalid_result.is_ok());
assert!(!invalid_result.unwrap());
// 测试不存在的文件
let nonexistent_result = processor.validate_image_file("/nonexistent/file.jpg").await;
assert!(nonexistent_result.is_ok());
assert!(!nonexistent_result.unwrap());
}
#[tokio::test]
async fn test_extract_image_paths_from_markdown() {
let markdown_content = r#"
# Test Document
![Image 1](images/test1.jpg)
![Image 2](./local/test2.png)
![External Image](https://example.com/image.jpg)
![Another Image](../parent/test3.gif)
Some text here.
"#;
let paths = ImageProcessor::extract_image_paths(markdown_content);
// 应该提取到3个本地图片路径排除外部URL
assert_eq!(paths.len(), 3);
assert!(paths.contains(&"images/test1.jpg".to_string()));
assert!(paths.contains(&"./local/test2.png".to_string()));
assert!(paths.contains(&"../parent/test3.gif".to_string()));
}
#[tokio::test]
async fn test_cache_functionality() {
let temp_dir = TempDir::new().unwrap();
let config = ImageProcessorConfig::default();
let processor = ImageProcessor::new(config, None);
// 初始缓存应该为空
let (total, successful) = processor.get_cache_stats().await;
assert_eq!(total, 0);
assert_eq!(successful, 0);
// 尝试上传一些图片由于没有OSS服务会失败且不会被缓存
let test_images = create_test_images(&temp_dir).await;
let result = processor.batch_upload_images(test_images).await;
// 验证上传结果
assert!(result.is_ok());
let upload_results = result.unwrap();
assert!(!upload_results.is_empty());
// 由于没有OSS服务所有上传都应该失败
for upload_result in &upload_results {
assert!(!upload_result.success);
assert!(upload_result.error_message.is_some());
}
// 检查缓存统计(失败的上传不会被缓存)
let (total_after, successful_after) = processor.get_cache_stats().await;
assert_eq!(total_after, 0); // 失败的上传不会被缓存
assert_eq!(successful_after, 0);
// 清空缓存(即使为空也应该正常工作)
processor.clear_cache().await;
let (total_cleared, successful_cleared) = processor.get_cache_stats().await;
assert_eq!(total_cleared, 0);
assert_eq!(successful_cleared, 0);
}
// 辅助函数
async fn create_test_images(temp_dir: &TempDir) -> Vec<String> {
let mut images = Vec::new();
for (i, ext) in ["jpg", "png", "gif"].iter().enumerate() {
let image_path = temp_dir.path().join(format!("test_{i}.{ext}"));
create_test_image(&image_path).await;
images.push(image_path.to_string_lossy().to_string());
}
images
}
async fn create_test_image(path: &Path) {
// 创建模拟图片文件(简单的二进制数据)
let mut content = Vec::new();
// 根据扩展名添加相应的文件头
if let Some(ext) = path.extension().and_then(|e| e.to_str()) {
match ext.to_lowercase().as_str() {
"jpg" | "jpeg" => {
content.extend_from_slice(&[0xFF, 0xD8, 0xFF, 0xE0]); // JPEG header
}
"png" => {
content.extend_from_slice(&[0x89, 0x50, 0x4E, 0x47]); // PNG header
}
"gif" => {
content.extend_from_slice(&[0x47, 0x49, 0x46, 0x38]); // GIF header
}
_ => {
content.extend_from_slice(&[0xFF, 0xD8, 0xFF, 0xE0]); // Default to JPEG
}
}
}
// 添加一些随机数据
for i in 0..1024 {
content.push((i % 256) as u8);
}
tokio::fs::write(path, &content).await.unwrap();
}

View File

@@ -0,0 +1,596 @@
use document_parser::utils::{
health_check::{
EnhancedHealthCheckManager, HealthCheckConfig, HealthCheckResult, HealthChecker,
HealthStatus,
},
logging::{
CorrelationContext, EnhancedLoggingSystem, LogFormat, LogOutputTarget, LoggingConfig,
},
metrics::{AsyncMetricsCollector, MetricsRegistry, PerformanceMonitor},
};
use std::collections::HashMap;
use std::sync::Arc;
use std::time::Duration;
use tokio::time::sleep;
/// 模拟健康检查器
struct MockHealthChecker {
name: String,
should_fail: std::sync::Arc<std::sync::atomic::AtomicBool>,
response_delay: Duration,
}
impl MockHealthChecker {
fn new(name: String, response_delay: Duration) -> Self {
Self {
name,
should_fail: Arc::new(std::sync::atomic::AtomicBool::new(false)),
response_delay,
}
}
fn set_should_fail(&self, should_fail: bool) {
self.should_fail
.store(should_fail, std::sync::atomic::Ordering::Relaxed);
}
}
#[async_trait::async_trait]
impl HealthChecker for MockHealthChecker {
async fn check_health(&self) -> HealthCheckResult {
// 模拟检查延迟
sleep(self.response_delay).await;
let status = if self.should_fail.load(std::sync::atomic::Ordering::Relaxed) {
HealthStatus::Unhealthy
} else {
HealthStatus::Healthy
};
let mut result = HealthCheckResult::new(
self.name.clone(),
status,
format!("Mock health check for {}", self.name),
);
result.add_detail("mock".to_string(), "true".to_string());
result.add_detail(
"delay_ms".to_string(),
self.response_delay.as_millis().to_string(),
);
result.with_response_time(self.response_delay)
}
fn component_name(&self) -> &str {
&self.name
}
fn timeout(&self) -> Duration {
Duration::from_secs(5)
}
}
#[tokio::test]
async fn test_enhanced_logging_system() {
// 创建临时日志文件
let temp_dir = tempfile::tempdir().unwrap();
let log_file = temp_dir.path().join("test.log");
let config = LoggingConfig {
level: "debug".to_string(),
format: LogFormat::Json,
output: LogOutputTarget::Both,
file_path: Some(log_file.to_string_lossy().to_string()),
enable_console: true,
enable_json: true,
enable_correlation: true,
service_name: "test-service".to_string(),
service_version: "1.0.0".to_string(),
environment: "test".to_string(),
..Default::default()
};
// 初始化日志系统
let logging_system = EnhancedLoggingSystem::init(config).unwrap();
// 设置关联上下文
let correlation = CorrelationContext::new()
.with_request_id("req-123".to_string())
.with_task_id("task-456".to_string())
.with_user_id("user-789".to_string());
logging_system.set_correlation_context(correlation).await;
// 生成一些日志
tracing::info!("Test information log");
tracing::warn!(error_code = "E001", "Test warning log");
tracing::error!(component = "test", "Test error log");
// 创建带有关联上下文的span
let span = logging_system.create_span("test_operation").await;
let _enter = span.enter();
tracing::info!("Log in span");
// 等待日志写入
sleep(Duration::from_millis(100)).await;
// 验证日志文件存在(在某些测试环境中可能不会创建文件)
if log_file.exists() {
// 读取日志文件内容
let log_content = tokio::fs::read_to_string(&log_file).await.unwrap();
assert!(!log_content.is_empty());
// 验证日志内容包含预期的信息
assert!(log_content.contains("req-123"));
assert!(log_content.contains("task-456"));
assert!(log_content.contains("user-789"));
assert!(log_content.contains("test-service"));
println!(
"Log content preview:\\n{}",
&log_content[..log_content.len().min(500)]
);
} else {
// 如果文件不存在,至少验证日志系统已初始化
tracing::info!("The log file is not created, but the logging system works normally");
}
}
#[tokio::test]
async fn test_metrics_registry_and_async_collector() {
let registry = Arc::new(MetricsRegistry::new());
// 注册一些测试指标
let counter = registry
.register_counter(
"test_counter".to_string(),
HashMap::from([("service".to_string(), "test".to_string())]),
)
.await;
let gauge = registry
.register_gauge("test_gauge".to_string(), HashMap::new())
.await;
let histogram = registry
.register_histogram(
"test_histogram".to_string(),
vec![0.1, 0.5, 1.0, 2.0, 5.0],
HashMap::new(),
)
.await;
// 使用指标
counter.inc();
counter.add(5);
assert_eq!(counter.get(), 6);
gauge.set(42);
gauge.inc();
assert_eq!(gauge.get(), 43);
histogram.observe(0.3);
histogram.observe(1.5);
histogram.observe(3.0);
assert_eq!(histogram.get_count(), 3);
// 测试异步指标收集器
let collector = AsyncMetricsCollector::new(registry.clone(), Duration::from_millis(100));
collector.start().await.unwrap();
// 等待几个收集周期
sleep(Duration::from_millis(300)).await;
collector.stop();
// 导出指标
let prometheus_export = registry.export_prometheus().await;
assert!(prometheus_export.contains("test_counter"));
assert!(prometheus_export.contains("test_gauge"));
assert!(prometheus_export.contains("test_histogram"));
let json_export = registry.export_json().await.unwrap();
assert!(json_export.contains("test_counter"));
assert!(json_export.contains("test_gauge"));
assert!(json_export.contains("test_histogram"));
println!("Prometheus export:\\n{prometheus_export}");
println!("JSON export:\\n{json_export}");
}
#[tokio::test]
async fn test_performance_monitor_with_async_collection() {
let registry = Arc::new(MetricsRegistry::new());
let monitor =
PerformanceMonitor::with_async_collector(registry.clone(), Duration::from_millis(50));
// 初始化标准指标
monitor.init_standard_metrics().await;
// 启动异步收集
monitor.start_collection().await.unwrap();
// 模拟一些活动
monitor
.record_http_request("GET", 200, Duration::from_millis(150))
.await;
monitor
.record_http_request("POST", 201, Duration::from_millis(300))
.await;
monitor
.record_http_request("GET", 404, Duration::from_millis(50))
.await;
monitor
.record_task_processing(Duration::from_secs(5), true)
.await;
monitor
.record_task_processing(Duration::from_secs(10), false)
.await;
monitor.update_active_tasks(3).await;
monitor.update_memory_usage(1024 * 1024 * 512).await; // 512MB
monitor.update_cpu_usage(0.75).await; // 75%
// 等待指标收集
sleep(Duration::from_millis(200)).await;
// 验证指标
let http_counter = registry.get_counter("http_requests_total").await;
assert!(http_counter.is_some());
let task_counter = registry.get_counter("tasks_processed_total").await;
assert!(task_counter.is_some());
assert_eq!(task_counter.unwrap().get(), 2);
let active_tasks_gauge = registry.get_gauge("tasks_active").await;
assert!(active_tasks_gauge.is_some());
assert_eq!(active_tasks_gauge.unwrap().get(), 3);
// 停止收集
monitor.stop_collection();
// 导出指标验证
let metrics_export = registry.export_json().await.unwrap();
assert!(metrics_export.contains("http_requests_total"));
assert!(metrics_export.contains("tasks_processed_total"));
assert!(metrics_export.contains("tasks_active"));
println!("Performance monitoring indicators:\\n{metrics_export}");
}
#[tokio::test]
async fn test_enhanced_health_check_manager() {
let config = HealthCheckConfig {
check_interval: Duration::from_millis(100),
timeout: Duration::from_millis(500),
enable_detailed_checks: true,
enable_system_metrics: true,
..Default::default()
};
let registry = Arc::new(MetricsRegistry::new());
let manager = EnhancedHealthCheckManager::new(config).with_metrics(registry.clone());
// 注册模拟健康检查器
let checker1 = Arc::new(MockHealthChecker::new(
"service1".to_string(),
Duration::from_millis(50),
));
let checker2 = Arc::new(MockHealthChecker::new(
"service2".to_string(),
Duration::from_millis(100),
));
let checker3 = Arc::new(MockHealthChecker::new(
"service3".to_string(),
Duration::from_millis(150),
));
manager.register_checker(checker1.clone()).await;
manager.register_checker(checker2.clone()).await;
manager.register_checker(checker3.clone()).await;
assert_eq!(manager.get_checker_count().await, 3);
// 执行健康检查
let status = manager.check_all().await;
assert_eq!(status.overall_status, HealthStatus::Healthy);
assert_eq!(status.healthy_count, 3);
assert_eq!(status.unhealthy_count, 0);
// 设置一个检查器失败
checker2.set_should_fail(true);
let status = manager.check_all().await;
assert_eq!(status.overall_status, HealthStatus::Unhealthy);
assert_eq!(status.healthy_count, 2);
assert_eq!(status.unhealthy_count, 1);
// 测试单个组件检查
let component_result = manager.check_component("service1").await;
assert!(component_result.is_some());
assert!(component_result.unwrap().is_healthy());
let component_result = manager.check_component("service2").await;
assert!(component_result.is_some());
assert!(component_result.unwrap().is_unhealthy());
// 测试不存在的组件
let component_result = manager.check_component("nonexistent").await;
assert!(component_result.is_none());
// 启动定期检查
manager.start_periodic_checks().await.unwrap();
assert!(manager.is_running());
// 等待几个检查周期
sleep(Duration::from_millis(350)).await;
// 获取最后的检查结果
let last_check = manager.get_last_check().await;
assert!(last_check.is_some());
let last_status = last_check.unwrap();
assert_eq!(last_status.components.len(), 3);
// 停止定期检查
manager.stop_periodic_checks();
assert!(!manager.is_running());
// 验证健康检查指标
let health_counter = registry.get_counter("health_checks_total").await;
if let Some(counter) = health_counter {
assert!(counter.get() > 0);
println!("Number of health check executions: {}", counter.get());
}
println!("Last health check status: {:?}", last_status.overall_status);
}
#[tokio::test]
async fn test_health_check_timeout_handling() {
let config = HealthCheckConfig {
check_interval: Duration::from_millis(200),
timeout: Duration::from_millis(100), // 短超时时间
..Default::default()
};
let manager = EnhancedHealthCheckManager::new(config);
// 注册一个响应慢的检查器
let slow_checker = Arc::new(MockHealthChecker::new(
"slow_service".to_string(),
Duration::from_millis(200), // 超过超时时间
));
manager.register_checker(slow_checker).await;
// 执行健康检查
let status = manager.check_all().await;
assert_eq!(status.overall_status, HealthStatus::Unhealthy);
assert_eq!(status.unhealthy_count, 1);
// 验证超时消息
let component = status.get_component_status("slow_service").unwrap();
assert!(component.message.contains("timeout"));
println!("Timeout check result: {component:?}");
}
#[tokio::test]
#[ignore = "Sets global tracing subscriber, conflicts with other tests"]
async fn test_correlation_context_propagation() {
let config = LoggingConfig {
level: "info".to_string(),
enable_correlation: true,
..Default::default()
};
let logging_system = EnhancedLoggingSystem::init(config).unwrap();
// 生成关联ID
let request_id = logging_system.generate_request_id().await;
let trace_id = logging_system.generate_trace_id().await;
assert!(!request_id.is_empty());
assert!(!trace_id.is_empty());
// 获取关联上下文
let context = logging_system.get_correlation_context().await;
assert_eq!(context.request_id, Some(request_id.clone()));
assert_eq!(context.trace_id, Some(trace_id.clone()));
// 创建带有关联上下文的span
let span = logging_system.create_span("test_correlation").await;
let _enter = span.enter();
tracing::info!("Test associated context propagation");
// 验证关联字段
let fields = context.to_fields();
assert!(fields.contains_key("request_id"));
assert!(fields.contains_key("trace_id"));
assert_eq!(fields.get("request_id"), Some(&request_id));
assert_eq!(fields.get("trace_id"), Some(&trace_id));
println!("Associated context field: {fields:?}");
}
#[tokio::test]
async fn test_metrics_export_formats() {
let registry = Arc::new(MetricsRegistry::new());
// 创建各种类型的指标
let counter = registry
.register_counter(
"export_test_counter".to_string(),
HashMap::from([
("service".to_string(), "test".to_string()),
("version".to_string(), "1.0".to_string()),
]),
)
.await;
let gauge = registry
.register_gauge(
"export_test_gauge".to_string(),
HashMap::from([("unit".to_string(), "bytes".to_string())]),
)
.await;
let histogram = registry
.register_histogram(
"export_test_histogram".to_string(),
vec![0.1, 0.5, 1.0, 2.0, 5.0, 10.0],
HashMap::from([("operation".to_string(), "test".to_string())]),
)
.await;
let summary = registry
.register_summary("export_test_summary".to_string(), 1000, HashMap::new())
.await;
// 添加一些数据
counter.add(42);
gauge.set(1024);
histogram.observe(0.3);
histogram.observe(1.5);
histogram.observe(3.0);
histogram.observe(7.0);
summary.observe(0.1).await;
summary.observe(0.5).await;
summary.observe(1.2).await;
summary.observe(2.8).await;
// 测试Prometheus格式导出
let prometheus_export = registry.export_prometheus().await;
// 验证Prometheus格式
assert!(prometheus_export.contains("# TYPE export_test_counter counter"));
assert!(prometheus_export.contains("# TYPE export_test_gauge gauge"));
assert!(prometheus_export.contains("# TYPE export_test_histogram histogram"));
assert!(prometheus_export.contains("export_test_counter{service=\"test\",version=\"1.0\"} 42"));
assert!(prometheus_export.contains("export_test_gauge{unit=\"bytes\"} 1024"));
assert!(prometheus_export.contains("export_test_histogram_bucket"));
assert!(prometheus_export.contains("export_test_histogram_sum"));
assert!(prometheus_export.contains("export_test_histogram_count"));
// 测试JSON格式导出
let json_export = registry.export_json().await.unwrap();
let json_value: serde_json::Value = serde_json::from_str(&json_export).unwrap();
// 验证JSON结构
assert!(json_value["counters"]["export_test_counter"].is_object());
assert!(json_value["gauges"]["export_test_gauge"].is_object());
assert!(json_value["histograms"]["export_test_histogram"].is_object());
assert!(json_value["summaries"]["export_test_summary"].is_object());
// 验证数据值
assert_eq!(json_value["counters"]["export_test_counter"]["value"], 42);
assert_eq!(json_value["gauges"]["export_test_gauge"]["value"], 1024);
assert_eq!(
json_value["histograms"]["export_test_histogram"]["count"],
4
);
println!("Prometheus export format:\\n{prometheus_export}");
println!("JSON export format:\\n{json_export}");
}
#[tokio::test]
#[ignore = "Sets global tracing subscriber, conflicts with other tests"]
async fn test_integrated_monitoring_system() {
// 创建完整的监控系统
let registry = Arc::new(MetricsRegistry::new());
// 初始化日志系统
let logging_config = LoggingConfig {
level: "info".to_string(),
enable_correlation: true,
service_name: "integrated-test".to_string(),
..Default::default()
};
let logging_system = EnhancedLoggingSystem::init(logging_config).unwrap();
// 初始化性能监控
let monitor =
PerformanceMonitor::with_async_collector(registry.clone(), Duration::from_millis(50));
monitor.init_standard_metrics().await;
monitor.start_collection().await.unwrap();
// 初始化健康检查
let health_config = HealthCheckConfig {
check_interval: Duration::from_millis(100),
..Default::default()
};
let health_manager =
EnhancedHealthCheckManager::new(health_config).with_metrics(registry.clone());
// 注册健康检查器
let checker = Arc::new(MockHealthChecker::new(
"integrated_service".to_string(),
Duration::from_millis(10),
));
health_manager.register_checker(checker).await;
// 启动健康检查
health_manager.start_periodic_checks().await.unwrap();
// 设置关联上下文
let request_id = logging_system.generate_request_id().await;
let correlation = CorrelationContext::new()
.with_request_id(request_id.clone())
.with_task_id("integration-test-task".to_string());
logging_system.set_correlation_context(correlation).await;
// 模拟一些系统活动
let span = logging_system.create_span("integration_test").await;
let _enter = span.enter();
tracing::info!("Start integration testing");
// 记录一些指标
monitor
.record_http_request("GET", 200, Duration::from_millis(100))
.await;
monitor
.record_task_processing(Duration::from_secs(2), true)
.await;
monitor.update_active_tasks(5).await;
// 等待系统运行
sleep(Duration::from_millis(300)).await;
tracing::info!("Integration tests running");
// 检查健康状态
let health_status = health_manager.check_all().await;
assert!(health_status.is_healthy());
// 获取指标
let metrics_json = registry.export_json().await.unwrap();
assert!(metrics_json.contains("http_requests_total"));
assert!(metrics_json.contains("tasks_processed_total"));
tracing::info!("Integration testing completed");
// 清理
monitor.stop_collection();
health_manager.stop_periodic_checks();
println!("Integration test request ID: {request_id}");
println!("Health status: {:?}", health_status.overall_status);
println!(
"Indicator summary: {} indicator types",
serde_json::from_str::<serde_json::Value>(&metrics_json)
.unwrap()
.as_object()
.unwrap()
.len()
);
}