use postfix_log_parser::{events::base::PostfixLogLevel, MasterParser};
use std::fs::{self, File};
use std::io::Write;
fn get_real_log_lines() -> Vec<String> {
let log_content = fs::read_to_string("logs/test1.log")
.expect("无法读取 logs/test1.log 文件,请确保日志文件存在");
let mut lines: Vec<String> = log_content
.lines()
.filter(|line| !line.trim().is_empty())
.map(|line| line.to_string())
.collect();
if !log_content.ends_with('\n') && !log_content.is_empty() {
if let Some(last_part) = log_content.split('\n').last() {
if !last_part.trim().is_empty() && !lines.iter().any(|line| line == last_part) {
lines.push(last_part.to_string());
}
}
}
lines
}
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_parse_base_info() {
let parser = MasterParser::new();
let real_logs = get_real_log_lines();
let log_line = &real_logs[0];
let base_info = parser.parse_base_info(log_line).unwrap();
let mut output = String::new();
output.push_str("=== Master Parser - 基础信息解析测试结果 ===\n\n");
output.push_str("测试时间: 2024-12-19\n");
output.push_str(&format!("测试日志: {}\n\n", log_line));
output.push_str("解析结果:\n");
output.push_str(&format!(" 主机名: {}\n", base_info.hostname));
output.push_str(&format!(" 组件: {}\n", base_info.component));
output.push_str(&format!(" 进程ID: {}\n", base_info.process_id));
output.push_str(&format!(" 日志等级: {:?}\n", base_info.log_level));
output.push_str(&format!(" 消息内容: {}\n", base_info.message));
output.push_str(&format!(
" 时间戳: {}\n",
base_info.timestamp.format("%Y-%m-%d %H:%M:%S")
));
output.push_str("\n断言验证:\n");
output.push_str(&format!(
" ✅ 主机名 = 'm01': {}\n",
base_info.hostname == "m01"
));
output.push_str(&format!(
" ✅ 组件 = 'smtpd': {}\n",
base_info.component == "smtpd"
));
output.push_str(&format!(
" ✅ 进程ID = 89: {}\n",
base_info.process_id == 89
));
output.push_str(&format!(
" ✅ 日志等级 = Info: {}\n",
base_info.log_level == PostfixLogLevel::Info
));
write_parse_result_to_file("master_parser_basic_info.txt", &output);
assert_eq!(base_info.hostname, "m01");
assert_eq!(base_info.component, "smtpd");
assert_eq!(base_info.process_id, 89);
assert_eq!(base_info.log_level, PostfixLogLevel::Info);
assert_eq!(
base_info.message,
"disconnect from localhost[127.0.0.1]:60090 ehlo=2 mail=1 rcpt=1 bdat=1 quit=1 commands=6"
);
}
#[test]
fn test_log_level_extraction() {
let parser = MasterParser::new();
let real_logs = get_real_log_lines();
let mut output = String::new();
output.push_str("=== Master Parser - 日志等级提取测试结果 ===\n\n");
output.push_str("测试时间: 2024-12-19\n");
let warning_log = real_logs
.iter()
.find(|line| line.contains("warning:"))
.expect("应该存在warning级别的日志");
output.push_str("\n警告日志测试:\n");
output.push_str(&format!(" 日志内容: {}\n", warning_log));
let base_info = parser.parse_base_info(warning_log).unwrap();
output.push_str(&format!(" 解析等级: {:?}\n", base_info.log_level));
output.push_str(&format!(" 消息内容: {}\n", base_info.message));
output.push_str(&format!(
" ✅ 等级为Warning: {}\n",
base_info.log_level == PostfixLogLevel::Warning
));
output.push_str(&format!(
" ✅ 包含dict_nis_init: {}\n",
base_info.message.contains("dict_nis_init")
));
let info_log = real_logs
.iter()
.find(|line| !line.contains("warning:"))
.expect("应该存在Info级别的日志");
output.push_str("\nInfo日志测试:\n");
output.push_str(&format!(" 日志内容: {}\n", info_log));
let base_info_info = parser.parse_base_info(info_log).unwrap();
output.push_str(&format!(" 解析等级: {:?}\n", base_info_info.log_level));
output.push_str(&format!(
" ✅ 等级为Info: {}\n",
base_info_info.log_level == PostfixLogLevel::Info
));
write_parse_result_to_file("master_parser_log_levels.txt", &output);
assert_eq!(base_info.log_level, PostfixLogLevel::Warning);
assert!(base_info.message.contains("dict_nis_init"));
assert_eq!(base_info_info.log_level, PostfixLogLevel::Info);
}
#[test]
fn test_component_recognition() {
let parser = MasterParser::new();
let real_logs = get_real_log_lines();
let mut output = String::new();
output.push_str("=== Master Parser - 组件识别测试结果 ===\n\n");
output.push_str("测试时间: 2024-12-19\n");
let test_cases = vec![
("smtpd", "postfix/smtpd["),
("qmgr", "postfix/qmgr["),
("smtp", "postfix/smtp["),
("cleanup", "postfix/cleanup["),
];
for (expected_component, search_pattern) in test_cases {
let component_log = real_logs
.iter()
.find(|line| line.contains(search_pattern))
.expect(&format!("应该存在{}组件的日志", expected_component));
output.push_str(&format!("\n{} 组件测试:\n", expected_component));
output.push_str(&format!(" 搜索模式: {}\n", search_pattern));
output.push_str(&format!(" 找到日志: {}\n", component_log));
let base_info = parser.parse_base_info(component_log).unwrap();
output.push_str(&format!(" 解析组件: {}\n", base_info.component));
output.push_str(&format!(" 进程ID: {}\n", base_info.process_id));
output.push_str(&format!(
" ✅ 组件匹配: {}\n",
base_info.component == expected_component
));
assert_eq!(base_info.component, expected_component);
}
write_parse_result_to_file("master_parser_components.txt", &output);
}
#[test]
fn test_all_real_logs_parsing() {
let parser = MasterParser::new();
let real_logs = get_real_log_lines();
let mut successful_parses = 0;
let mut failed_parses = 0;
let mut component_stats = std::collections::HashMap::new();
let mut output = String::new();
output.push_str("=== Master Parser - 全量真实日志解析测试结果 ===\n\n");
output.push_str("测试时间: 2024-12-19\n");
output.push_str(&format!("总日志数: {}\n\n", real_logs.len()));
output.push_str("解析详情:\n");
for (line_num, log_line) in real_logs.iter().enumerate() {
match parser.parse_base_info(log_line) {
Ok(base_info) => {
successful_parses += 1;
*component_stats
.entry(base_info.component.clone())
.or_insert(0) += 1;
assert!(!base_info.hostname.is_empty());
assert!(!base_info.component.is_empty());
assert!(base_info.process_id > 0);
assert!(!base_info.message.is_empty());
if line_num < 10 || line_num >= real_logs.len() - 10 {
output.push_str(&format!(
" [{}] ✅ {} | {}[{}] | {}\n",
line_num + 1,
base_info.timestamp.format("%H:%M:%S"),
base_info.component,
base_info.process_id,
if base_info.message.len() > 50 {
format!("{}...", &base_info.message[..50])
} else {
base_info.message.clone()
}
));
}
}
Err(e) => {
failed_parses += 1;
output.push_str(&format!(
" [{}] ❌ 解析失败: {} - 错误: {}\n",
line_num + 1,
if log_line.len() > 50 {
&log_line[..50]
} else {
log_line
},
e
));
}
}
}
if real_logs.len() > 20 {
output.push_str(&format!(
" ... (省略中间{}行详情) ...\n",
real_logs.len() - 20
));
}
output.push_str("\n统计结果:\n");
output.push_str(&format!(" 成功解析: {}\n", successful_parses));
output.push_str(&format!(" 解析失败: {}\n", failed_parses));
output.push_str(&format!(" 总数: {}\n", real_logs.len()));
output.push_str(&format!(
" 成功率: {:.2}%\n",
(successful_parses as f64 / real_logs.len() as f64) * 100.0
));
output.push_str("\n组件分布:\n");
for (component, count) in &component_stats {
let percentage = (*count as f64 / successful_parses as f64) * 100.0;
output.push_str(&format!(
" {}: {} 条 ({:.1}%)\n",
component, count, percentage
));
}
write_parse_result_to_file("master_parser_full_analysis.txt", &output);
println!("真实日志解析统计:");
println!(" 成功: {}", successful_parses);
println!(" 失败: {}", failed_parses);
println!(" 总数: {}", real_logs.len());
assert!(successful_parses >= 399, "应该成功解析至少399行真实日志");
assert_eq!(failed_parses, 0, "不应该有解析失败的日志");
}
#[test]
fn test_invalid_format() {
let parser = MasterParser::new();
let mut output = String::new();
output.push_str("=== Master Parser - 无效格式测试结果 ===\n\n");
output.push_str("测试时间: 2024-12-19\n");
let invalid_logs = vec![
"",
"invalid log line",
"Jun 05 17:24:32 m01 not-postfix[123]: message",
"Jun 05 invalid format",
];
output.push_str("\n无效日志测试:\n");
for invalid_log in invalid_logs {
if invalid_log.is_empty() {
output.push_str(" 跳过空行测试\n");
continue;
}
output.push_str(&format!(" 测试日志: '{}'\n", invalid_log));
let result = parser.parse_base_info(invalid_log);
let is_error = result.is_err();
match result {
Ok(_) => {
output.push_str(" ❌ 意外解析成功(应该失败)\n");
}
Err(e) => {
output.push_str(&format!(" ✅ 正确失败: {}\n", e));
}
}
assert!(is_error, "应该解析失败: {}", invalid_log);
}
write_parse_result_to_file("master_parser_invalid_format.txt", &output);
}