use chrono::Utc;
use postfix_log_parser::events::base::{
ComponentEvent, PostfixLogEvent, PostfixLogLevel, UnknownEvent,
};
use postfix_log_parser::{parse_log_line, 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_log_level_severity() {
let mut output = String::new();
output.push_str("=== Events Base - 日志等级严重程度测试结果 ===\n\n");
output.push_str("测试时间: 2024-12-19\n");
let levels = vec![
(PostfixLogLevel::Debug, 1),
(PostfixLogLevel::Info, 2),
(PostfixLogLevel::Warning, 3),
(PostfixLogLevel::Error, 4),
(PostfixLogLevel::Fatal, 5),
(PostfixLogLevel::Panic, 6),
];
output.push_str("\n严重程度测试:\n");
for (level, expected_severity) in &levels {
let actual_severity = level.severity_level();
output.push_str(&format!(
" {:?}: 期望={}, 实际={}, ✅ 匹配={}\n",
level,
expected_severity,
actual_severity,
actual_severity == *expected_severity
));
assert_eq!(actual_severity, *expected_severity);
}
write_parse_result_to_file("events_base_severity.txt", &output);
}
#[test]
fn test_log_level_methods() {
let warning = PostfixLogLevel::Warning;
assert!(warning.is_warning_level());
assert!(!warning.is_error_level());
assert!(!warning.is_debug_level());
let error = PostfixLogLevel::Error;
assert!(!error.is_warning_level());
assert!(error.is_error_level());
assert!(!error.is_debug_level());
let debug = PostfixLogLevel::Debug;
assert!(debug.is_debug_level());
assert!(!debug.is_error_level());
assert!(!debug.is_warning_level());
}
#[test]
fn test_log_level_from_prefix() {
assert_eq!(
PostfixLogLevel::from_prefix("warning:"),
Some(PostfixLogLevel::Warning)
);
assert_eq!(
PostfixLogLevel::from_prefix("error:"),
Some(PostfixLogLevel::Error)
);
assert_eq!(
PostfixLogLevel::from_prefix("fatal:"),
Some(PostfixLogLevel::Fatal)
);
assert_eq!(
PostfixLogLevel::from_prefix("debug:"),
Some(PostfixLogLevel::Debug)
);
assert_eq!(
PostfixLogLevel::from_prefix("panic:"),
Some(PostfixLogLevel::Panic)
);
assert_eq!(PostfixLogLevel::from_prefix("anything else"), None);
}
#[test]
fn test_log_level_to_string() {
assert_eq!(PostfixLogLevel::Warning.to_string(), "warning");
assert_eq!(PostfixLogLevel::Error.to_string(), "error");
assert_eq!(PostfixLogLevel::Info.to_string(), "info");
assert_eq!(PostfixLogLevel::Debug.to_string(), "debug");
}
#[test]
fn test_postfix_log_event_creation_from_real_logs() {
let real_logs = get_real_log_lines();
let parser = MasterParser::new();
let log_line = &real_logs[0];
let base_info = parser.parse_base_info(log_line).unwrap();
let unknown_event = UnknownEvent {
component: base_info.component.clone(),
message: base_info.message.clone(),
};
let event = PostfixLogEvent::new(
log_line.clone(),
Utc::now(),
base_info.hostname.clone(),
base_info.component.clone(),
base_info.process_id,
base_info.log_level,
ComponentEvent::Unknown(unknown_event),
None,
);
assert_eq!(event.hostname, "m01");
assert_eq!(event.component, "smtpd");
assert_eq!(event.process_id, 89);
assert_eq!(event.log_level, PostfixLogLevel::Info);
}
#[test]
fn test_postfix_log_event_convenience_methods_with_real_logs() {
let real_logs = get_real_log_lines();
let warning_log = real_logs
.iter()
.find(|line| line.contains("warning:"))
.expect("应该存在warning级别的日志");
let result = parse_log_line(warning_log);
let event = result.event.expect("应该解析成功");
assert!(event.is_warning_level());
assert!(!event.is_error_level());
assert!(!event.is_debug_level());
assert_eq!(event.severity_level(), 3);
}
#[test]
fn test_real_log_levels_distribution() {
let real_logs = get_real_log_lines();
let mut level_counts = std::collections::HashMap::new();
let mut output = String::new();
output.push_str("=== Events Base - 真实日志级别分布测试结果 ===\n\n");
output.push_str("测试时间: 2024-12-19\n");
output.push_str(&format!("总日志数: {}\n", real_logs.len()));
output.push_str("\n解析详情:\n");
let mut parsed_count = 0;
for (line_num, log_line) in real_logs.iter().enumerate() {
let result = parse_log_line(log_line);
if let Some(event) = result.event {
parsed_count += 1;
*level_counts.entry(event.log_level.clone()).or_insert(0) += 1;
if line_num < 5 || line_num >= real_logs.len() - 5 {
output.push_str(&format!(
" [{}] {:?} | {} | {}[{}]\n",
line_num + 1,
event.log_level,
event.timestamp.format("%H:%M:%S"),
event.component,
event.process_id
));
}
}
}
if real_logs.len() > 10 {
output.push_str(&format!(
" ... (省略中间{}行详情) ...\n",
real_logs.len() - 10
));
}
output.push_str("\n日志级别分布:\n");
for (level, count) in &level_counts {
let percentage = (*count as f64 / parsed_count as f64) * 100.0;
output.push_str(&format!(
" {:?}: {} 条 ({:.1}%)\n",
level, count, percentage
));
}
output.push_str(&format!("\n统计结果:\n"));
output.push_str(&format!(" 成功解析: {}\n", parsed_count));
output.push_str(&format!(" 总日志数: {}\n", real_logs.len()));
output.push_str(&format!(
" 解析率: {:.2}%\n",
(parsed_count as f64 / real_logs.len() as f64) * 100.0
));
write_parse_result_to_file("events_base_log_levels_distribution.txt", &output);
println!("真实日志级别分布:");
for (level, count) in &level_counts {
println!(" {:?}: {} 条", level, count);
}
assert!(level_counts.contains_key(&PostfixLogLevel::Info));
assert!(level_counts.contains_key(&PostfixLogLevel::Warning));
let total: i32 = level_counts.values().sum();
assert!(total >= 399, "应该有至少399条日志");
}
#[test]
fn test_log_level_enum_comprehensive() {
let all_levels = vec![
PostfixLogLevel::Debug,
PostfixLogLevel::Info,
PostfixLogLevel::Warning,
PostfixLogLevel::Error,
PostfixLogLevel::Fatal,
PostfixLogLevel::Panic,
];
for level in all_levels {
assert!(!level.to_string().is_empty());
assert!(!level.as_str().is_empty());
let severity = level.severity_level();
assert!(severity >= 1 && severity <= 6);
let display_str = format!("{}", level);
assert!(!display_str.is_empty());
}
assert_eq!(PostfixLogLevel::default(), PostfixLogLevel::Info);
}