use postfix_log_parser::{parse_log_line, MasterParser, ParseError};
use std::collections::HashMap;
use std::fs::{self, File};
use std::io::Write;
fn ensure_output_dir() {
if let Err(e) = fs::create_dir_all("test_outputs") {
eprintln!("警告: 无法创建输出目录: {}", e);
}
}
fn write_parse_result_to_file(filename: &str, content: &str) {
ensure_output_dir();
let filepath = format!("test_outputs/{}", filename);
match File::create(&filepath) {
Ok(mut file) => {
if let Err(e) = file.write_all(content.as_bytes()) {
eprintln!("警告: 无法写入文件 {}: {}", filepath, e);
} else {
println!("✅ 解析结果已输出到: {}", filepath);
}
}
Err(e) => {
eprintln!("警告: 无法创建文件 {}: {}", filepath, e);
}
}
}
#[test]
fn test_real_postfix_logs_basic_parsing() {
let log_content = match fs::read_to_string("logs/test1.log") {
Ok(content) => content,
Err(_) => {
println!("警告: 无法读取 logs/test1.log 文件,跳过集成测试");
return;
}
};
let mut successful_parses = 0;
let mut total_lines = 0;
let mut output = String::new();
output.push_str("=== Integration Basic Parsing - 真实Postfix日志基础解析测试 ===\n\n");
output.push_str("测试时间: 2024-12-19\n");
println!("开始解析实际的Postfix日志文件...");
for line in log_content.lines() {
if line.trim().is_empty() {
continue;
}
total_lines += 1;
let result = parse_log_line(line);
if result.confidence > 0.0 {
successful_parses += 1;
}
let total_line_count = log_content.lines().count();
if total_lines <= 5 || (total_line_count - total_lines <= 5) {
let log_display = if line.len() > 80 {
format!("{}...", &line[..80])
} else {
line.to_string()
};
output.push_str(&format!("\n[{}] 日志: {}\n", total_lines, log_display));
if result.confidence > 0.0 {
if let Some(event) = &result.event {
output.push_str(&format!(
" ✅ 成功解析 | {}[{}] | 置信度: {:.2}\n",
event.component, event.process_id, result.confidence
));
} else {
output.push_str(&format!(
" ✅ 部分解析 | 置信度: {:.2}\n",
result.confidence
));
}
} else {
output.push_str(&format!(
" ❌ 解析失败 | 错误: {:?}\n",
result.parsing_errors
));
}
}
}
output.push_str(&format!("\n解析统计:\n"));
output.push_str(&format!("总行数: {}\n", total_lines));
output.push_str(&format!("成功解析: {}\n", successful_parses));
output.push_str(&format!(
"成功率: {:.2}%\n",
(successful_parses as f64 / total_lines as f64) * 100.0
));
write_parse_result_to_file("integration_basic_parsing.txt", &output);
println!("\n解析结果统计:");
println!("总行数: {}", total_lines);
println!("成功解析: {}", successful_parses);
println!(
"成功率: {:.2}%",
(successful_parses as f64 / total_lines as f64) * 100.0
);
assert_eq!(successful_parses, total_lines, "所有日志都应该解析成功");
assert!(total_lines >= 399, "测试日志应该有至少399行");
}
#[test]
fn test_specific_log_lines_from_test1() {
let test_cases = vec![
(
"Jun 05 17:24:32 m01 postfix/smtpd[147]: ED7F32B031E3: client=localhost[127.0.0.1]:52392",
("m01", "smtpd", 147)
),
(
"Jun 05 17:24:32 m01 postfix/cleanup[141]: ED7F32B031E3: message-id=<v6hvzMZ6@example.com>",
("m01", "cleanup", 141)
),
(
"Jun 05 17:24:32 m01 postfix/qmgr[78]: ED7F32B031E3: from=<gzq@example.com>, size=94277, nrcpt=1 (queue active)",
("m01", "qmgr", 78)
),
(
"Jun 05 17:24:32 m01 postfix/smtp[148]: ED7F32B031E3: to=<m01@zcloud.center>, relay=mx.zcloud.center[192.168.2.229]:25, delay=0.01, delays=0/0/0/0, dsn=2.0.0, status=sent (250 OK)",
("m01", "smtp", 148)
),
];
let mut output = String::new();
output.push_str("=== Integration Basic Parsing - 特定日志行解析测试 ===\n\n");
output.push_str("测试时间: 2024-12-19\n");
for (log_line, (expected_hostname, expected_component, expected_pid)) in test_cases {
println!("测试日志行: {}", log_line);
output.push_str(&format!("\n测试日志: {}\n", log_line));
let result = parse_log_line(log_line);
assert!(result.confidence > 0.0, "解析失败: {:?}", result);
assert!(result.event.is_some(), "没有解析出事件: {:?}", result);
let event = result.event.unwrap();
output.push_str(&format!(
" 预期: 主机名={}, 组件={}, 进程ID={}\n",
expected_hostname, expected_component, expected_pid
));
output.push_str(&format!(
" 实际: 主机名={}, 组件={}, 进程ID={}\n",
event.hostname, event.component, event.process_id
));
output.push_str(&format!(" 置信度: {:.2}\n", result.confidence));
assert_eq!(event.hostname, expected_hostname);
assert_eq!(event.component, expected_component);
assert_eq!(event.process_id, expected_pid);
output.push_str(&format!(
" ✅ 解析成功: {} | {}[{}]\n",
event.timestamp.format("%H:%M:%S"),
event.component,
event.process_id
));
println!(
"✓ 解析成功: {} | {}[{}]",
event.timestamp.format("%H:%M:%S"),
event.component,
event.process_id
);
}
write_parse_result_to_file("integration_specific_logs.txt", &output);
}
#[test]
fn test_component_distribution() {
let log_content = match fs::read_to_string("logs/test1.log") {
Ok(content) => content,
Err(_) => {
println!("警告: 无法读取测试日志文件,跳过组件分布测试");
return;
}
};
let mut component_counts = HashMap::new();
let mut output = String::new();
output.push_str("=== Integration Basic Parsing - 组件分布分析测试 ===\n\n");
output.push_str("测试时间: 2024-12-19\n");
for line in log_content.lines() {
if line.trim().is_empty() {
continue;
}
let result = parse_log_line(line);
if let Some(event) = result.event {
*component_counts.entry(event.component).or_insert(0) += 1;
}
}
output.push_str(&format!("\nPostfix组件分布统计:\n"));
let total = component_counts.values().sum::<usize>();
let mut sorted_components: Vec<_> = component_counts.iter().collect();
sorted_components.sort_by(|a, b| b.1.cmp(a.1));
for (component, count) in &sorted_components {
let percentage = (**count as f64 / total as f64) * 100.0;
output.push_str(&format!(
" {}: {} 条日志 ({:.1}%)\n",
component, count, percentage
));
}
output.push_str(&format!("\n总计: {} 条日志\n", total));
output.push_str(&format!("\n组件验证:\n"));
output.push_str(&format!(
" ✅ 包含smtpd组件: {}\n",
component_counts.contains_key("smtpd")
));
output.push_str(&format!(
" ✅ 包含qmgr组件: {}\n",
component_counts.contains_key("qmgr")
));
output.push_str(&format!(
" ✅ 包含smtp组件: {}\n",
component_counts.contains_key("smtp")
));
output.push_str(&format!(
" ✅ 包含cleanup组件: {}\n",
component_counts.contains_key("cleanup")
));
write_parse_result_to_file("integration_component_distribution.txt", &output);
println!("\nPostfix组件分布统计:");
for (component, count) in &component_counts {
let percentage = (*count as f64 / total as f64) * 100.0;
println!(" {}: {} 条日志 ({:.1}%)", component, count, percentage);
}
assert!(component_counts.contains_key("smtpd"), "应包含smtpd组件");
assert!(component_counts.contains_key("qmgr"), "应包含qmgr组件");
assert!(component_counts.contains_key("smtp"), "应包含smtp组件");
assert!(
component_counts.contains_key("cleanup"),
"应包含cleanup组件"
);
let smtpd_count = component_counts.get("smtpd").unwrap_or(&0);
assert!(*smtpd_count > 0, "smtpd应该有日志记录");
}
#[test]
fn test_error_handling_and_edge_cases() {
let parser = MasterParser::new();
let mut output = String::new();
output.push_str("=== Integration Basic Parsing - 错误处理和边界情况测试 ===\n\n");
output.push_str("测试时间: 2024-12-19\n");
let error_cases = vec![
("", "空行"),
("invalid log line", "完全无效的日志"),
(
"Jun 05 17:24:32 m01 not-postfix[123]: message",
"非postfix日志",
),
("Jun 05 invalid format", "无效时间戳格式"),
(
"Jun 05 17:24:32 m01 postfix/smtpd[invalid]: message",
"无效进程ID",
),
];
for (log_line, description) in error_cases {
println!("测试错误情况: {}", description);
output.push_str(&format!("\n{}: '{}'\n", description, log_line));
if log_line.is_empty() {
output.push_str(" 跳过空行测试\n");
continue; }
let result = parser.parse_base_info(log_line);
match result {
Ok(base_info) => {
println!("! 意外解析成功: {}", base_info.component);
output.push_str(&format!(" ! 意外解析成功: {}\n", base_info.component));
}
Err(e) => {
println!("✓ 正确捕获错误: {}", e);
output.push_str(&format!(" ✓ 正确捕获错误: {}\n", e));
match e {
ParseError::InvalidLogFormat { .. } => {
println!(" 错误类型: 无效日志格式");
output.push_str(" 错误类型: 无效日志格式\n");
}
ParseError::InvalidTimestamp { .. } => {
println!(" 错误类型: 无效时间戳");
output.push_str(" 错误类型: 无效时间戳\n");
}
_ => {
println!(" 错误类型: 其他");
output.push_str(" 错误类型: 其他\n");
}
}
}
}
}
write_parse_result_to_file("integration_error_handling.txt", &output);
}