use chrono::Local;
use kerf::{Event, Level, Match, MatcherSet, matches, PatternMatcher};
use std::collections::HashMap;
use std::sync::Arc;
#[allow(clippy::too_many_arguments)]
fn create_test_event(
id: u64,
level: Level,
event: &str,
module_path: Option<&str>,
file: Option<&str>,
line: Option<u32>,
span_name: Option<&str>,
fields: Option<HashMap<String, String>>,
) -> Event {
Event {
id,
timestamp: Local::now(),
level,
target: "test_target".to_string(),
name: "test_event".to_string(),
module_path: module_path.map(|s| s.to_string()),
file: file.map(|s| s.to_string()),
line,
message: event.to_string(),
fields: fields.unwrap_or_default(),
span_name: span_name.map(|s| s.to_string()),
span_hierarchy: span_name.map(|s| s.to_string()), }
}
#[test]
fn test_trace_event_creation() {
let id = 1;
let level = Level::INFO;
let message = "Test event";
let module_path = Some("test_module");
let file = Some("test_file.rs");
let line = Some(42);
let span_name = Some("test_span");
let mut fields = HashMap::new();
fields.insert("test_key".to_string(), "test_value".to_string());
fields.insert("number".to_string(), "123".to_string());
let event = create_test_event(
id,
level,
message,
module_path,
file,
line,
span_name,
Some(fields.clone()),
);
assert_eq!(event.id, id);
assert_eq!(event.level, level);
assert_eq!(event.message, message);
assert_eq!(event.module_path, module_path.map(|s| s.to_string()));
assert_eq!(event.file, file.map(|s| s.to_string()));
assert_eq!(event.line, line);
assert_eq!(event.span_name, span_name.map(|s| s.to_string()));
assert_eq!(event.span_hierarchy, span_name.map(|s| s.to_string()));
assert_eq!(event.fields.len(), 2);
assert_eq!(event.fields.get("test_key").unwrap(), "test_value");
assert_eq!(event.fields.get("number").unwrap(), "123");
}
#[test]
fn test_trace_event_formatting() {
let event = create_test_event(
1,
Level::WARN,
"Warning event",
Some("test_module"),
Some("test_file.rs"),
Some(42),
Some("test_span"),
None,
);
let formatted = event.format();
assert!(formatted.contains("WARN"));
assert!(formatted.contains("Warning event"));
assert!(formatted.contains("[test_module]"));
assert!(formatted.contains("[span:test_span]"));
let formatted_with_file = event.format_with_file();
assert!(formatted_with_file.contains("test_file.rs:42"));
let mut fields = HashMap::new();
fields.insert("key1".to_string(), "value1".to_string());
fields.insert("key2".to_string(), "value2".to_string());
let event_with_fields = create_test_event(
2,
Level::ERROR,
"Error event",
Some("error_module"),
Some("error.rs"),
Some(100),
None,
Some(fields),
);
let formatted_with_fields = event_with_fields.format_with_fields();
assert!(formatted_with_fields.contains("key1=value1"));
assert!(formatted_with_fields.contains("key2=value2"));
}
#[test]
fn test_trace_event_display() {
let event = create_test_event(
1,
Level::INFO,
"Display test",
Some("display_module"),
Some("display.rs"),
Some(55),
None,
None,
);
let display_string = format!("{event}");
assert!(display_string.contains("INFO"));
assert!(display_string.contains("Display test"));
assert!(display_string.contains("[display_module]"));
}
#[test]
fn test_trace_event_arc_methods() {
let event = create_test_event(1, Level::DEBUG, "Arc test", None, None, None, None, None);
let arc_event = Arc::new(event);
assert_eq!(arc_event.ref_count(), 1);
let arc_event2 = arc_event.clone();
assert_eq!(arc_event.ref_count(), 2);
assert!(arc_event.ptr_eq(&arc_event2));
let different_event = Arc::new(create_test_event(
2,
Level::TRACE,
"Different event",
None,
None,
None,
None,
None,
));
assert!(!arc_event.ptr_eq(&different_event));
}
#[test]
fn test_pattern_matching() {
assert!(matches("exact", "exact"));
assert!(!matches("exact", "not_exact"));
assert!(matches("prefix*", "prefix"));
assert!(matches("prefix*", "prefix_with_more"));
assert!(!matches("prefix*", "wrong_prefix"));
assert!(matches("*suffix", "suffix"));
assert!(matches("*suffix", "with_suffix"));
assert!(!matches("*suffix", "wrong_suffix_extra"));
assert!(matches("pre*post", "prepost"));
assert!(matches("pre*post", "pre_middle_post"));
assert!(!matches("pre*post", "pre_wrong"));
assert!(!matches("pre*post", "wrong_post"));
assert!(matches("*mid*", "mid"));
assert!(matches("*mid*", "prefix_mid"));
assert!(matches("*mid*", "mid_suffix"));
assert!(matches("*mid*", "prefix_mid_suffix"));
assert!(!matches("*mid*", "nomatch"));
}
#[test]
fn test_trace_level() {
let trace_level = Level::INFO;
assert_eq!(trace_level.0, tracing::Level::INFO);
assert_eq!(format!("{trace_level}"), "INFO");
let filter: Level = trace_level;
assert_eq!(filter, Level::INFO);
let level_from_matcher = Level::DEBUG;
assert_eq!(level_from_matcher.0, tracing::Level::DEBUG);
assert_eq!(Level::WARN, Level(tracing::Level::WARN));
assert_ne!(Level::ERROR, Level(tracing::Level::INFO));
}
#[test]
fn test_trace_matcher() {
let info_event = create_test_event(
1,
Level::INFO,
"Info event",
Some("module_a"),
Some("file_a.rs"),
Some(10),
None,
None,
);
let debug_event = create_test_event(
2,
Level::DEBUG,
"Debug event",
Some("module_a"),
Some("file_a.rs"),
Some(20),
None,
None,
);
let error_event = create_test_event(
3,
Level::ERROR,
"Error event",
Some("module_b"),
Some("file_b.rs"),
Some(30),
None,
None,
);
let no_module_event = create_test_event(
4,
Level::WARN,
"No module",
None,
Some("unknown.rs"),
Some(40),
None,
None,
);
let span_event = create_test_event(
5,
Level::INFO,
"Span event",
Some("module_a"),
Some("file_a.rs"),
Some(50),
Some("test_span"),
None,
);
let level_matcher = Match::info().all_modules();
assert!(level_matcher.matches(&info_event)); assert!(!level_matcher.matches(&debug_event)); assert!(level_matcher.matches(&error_event)); assert!(level_matcher.matches(&no_module_event));
let module_matcher = Match::trace().module_pattern("module_a*");
assert!(module_matcher.matches(&info_event)); assert!(module_matcher.matches(&debug_event)); assert!(!module_matcher.matches(&error_event)); assert!(!module_matcher.matches(&no_module_event));
let file_matcher = Match::trace().file_pattern("file_b*");
assert!(!file_matcher.matches(&info_event)); assert!(!file_matcher.matches(&debug_event)); assert!(file_matcher.matches(&error_event)); assert!(!file_matcher.matches(&no_module_event));
let combined_matcher = Match::info()
.module_pattern("module_a*")
.file_pattern("file_a*");
assert!(combined_matcher.matches(&info_event)); assert!(!combined_matcher.matches(&debug_event)); assert!(!combined_matcher.matches(&error_event)); assert!(!combined_matcher.matches(&no_module_event));
let span_matcher = Match::info().all_modules().span_pattern("test_*");
assert!(!span_matcher.matches(&info_event)); assert!(span_matcher.matches(&span_event)); }
#[test]
fn test_target_pattern_matchering() {
let mut custom_fields = HashMap::new();
custom_fields.insert("key".to_string(), "value".to_string());
let event1 = create_test_event(
1,
Level::INFO,
"Standard target",
Some("module_a"),
Some("file_a.rs"),
Some(10),
None,
Some(custom_fields.clone()),
);
let mut event2 = create_test_event(
2,
Level::INFO,
"Custom target",
Some("module_a"),
Some("file_a.rs"),
Some(10),
None,
Some(custom_fields.clone()),
);
event2.target = "custom_target".to_string();
let mut event3 = create_test_event(
3,
Level::INFO,
"API target",
Some("module_a"),
Some("file_a.rs"),
Some(10),
None,
Some(custom_fields.clone()),
);
event3.target = "api_service".to_string();
let target_matcher = Match::info().all_modules().target_pattern("custom*");
assert!(!target_matcher.matches(&event1)); assert!(target_matcher.matches(&event2)); assert!(!target_matcher.matches(&event3));
let combined_matcher = Match::info()
.module_pattern("module_a*")
.target_pattern("api*");
assert!(!combined_matcher.matches(&event1)); assert!(!combined_matcher.matches(&event2)); assert!(combined_matcher.matches(&event3));
let multi_target_matcher = Match::info()
.all_modules()
.target_patterns(vec!["custom*", "api*"]);
assert!(!multi_target_matcher.matches(&event1)); assert!(multi_target_matcher.matches(&event2)); assert!(multi_target_matcher.matches(&event3));
let wildcard_target_matcher = Match::info().all_modules();
assert!(wildcard_target_matcher.matches(&event1)); assert!(wildcard_target_matcher.matches(&event2)); assert!(wildcard_target_matcher.matches(&event3)); }
#[test]
fn test_matcher_comparison() {
let filter1 = Match::info()
.module_patterns(vec!["pattern1", "pattern2"])
.file_pattern("file_pattern")
.span_pattern("span_pattern")
.target_pattern("target_pattern");
let filter2 = Match::info()
.module_patterns(vec!["pattern1", "pattern2"])
.file_pattern("file_pattern")
.span_pattern("span_pattern")
.target_pattern("target_pattern");
assert_eq!(filter1, filter2);
let filter3 = Match::debug()
.module_patterns(vec!["pattern1", "pattern2"])
.file_pattern("file_pattern")
.span_pattern("span_pattern")
.target_pattern("target_pattern");
assert_ne!(filter1, filter3);
let filter4 = Match::info()
.module_pattern("different")
.file_pattern("file_pattern")
.span_pattern("span_pattern")
.target_pattern("target_pattern");
assert_ne!(filter1, filter4);
let filter5 = Match::info()
.module_patterns(vec!["pattern1", "pattern2"])
.file_pattern("different")
.span_pattern("span_pattern")
.target_pattern("target_pattern");
assert_ne!(filter1, filter5);
let filter6 = Match::info()
.module_patterns(vec!["pattern1", "pattern2"])
.file_pattern("file_pattern")
.span_pattern("different_span")
.target_pattern("target_pattern");
assert_ne!(filter1, filter6);
let filter7 = Match::info()
.module_patterns(vec!["pattern1", "pattern2"])
.file_pattern("file_pattern")
.span_pattern("span_pattern")
.target_pattern("different_target");
assert_ne!(filter1, filter7);
}
#[test]
fn test_span_based_matchering() {
let no_span = create_test_event(
1,
Level::INFO,
"No span",
Some("module_a"),
Some("file.rs"),
Some(10),
None,
None,
);
let database_span = create_test_event(
2,
Level::INFO,
"Database span",
Some("module_a"),
Some("file.rs"),
Some(20),
Some("database_query"),
None,
);
let network_span = create_test_event(
3,
Level::INFO,
"Network span",
Some("module_a"),
Some("file.rs"),
Some(30),
Some("network_request"),
None,
);
let db_matcher = Match::info().all_modules().span_pattern("database_*");
assert!(!db_matcher.matches(&no_span)); assert!(db_matcher.matches(&database_span)); assert!(!db_matcher.matches(&network_span));
let filter_set = MatcherSet::empty()
.with_matcher(Match::info().all_modules())
.with_matcher(Match::info().exclude().span_pattern("network_*"));
let matchers = filter_set.iter_matchers();
let include_matcher = matchers
.iter()
.find(|m| m.include && m.span_patterns.is_empty())
.unwrap();
let exclude_matcher = matchers
.iter()
.find(|m| !m.include && m.span_patterns.contains(&"network_*".to_string()))
.unwrap();
assert!(include_matcher.matches(&no_span)); assert!(include_matcher.matches(&database_span)); assert!(include_matcher.matches(&network_span));
assert!(!exclude_matcher.matches(&no_span)); assert!(!exclude_matcher.matches(&database_span)); assert!(exclude_matcher.matches(&network_span));
let no_span_included = include_matcher.matches(&no_span);
let no_span_excluded = exclude_matcher.matches(&no_span);
assert!(no_span_included && !no_span_excluded);
let db_span_included = include_matcher.matches(&database_span);
let db_span_excluded = exclude_matcher.matches(&database_span);
assert!(db_span_included && !db_span_excluded);
let network_span_included = include_matcher.matches(&network_span);
let network_span_excluded = exclude_matcher.matches(&network_span);
assert!(network_span_included && network_span_excluded); }
#[test]
fn test_builder_pattern() {
let error_matcher = Match::error()
.exclude()
.module_pattern("internal*")
.module_pattern("test*");
assert_eq!(error_matcher.level, Level::ERROR);
assert!(!error_matcher.include);
assert_eq!(error_matcher.module_patterns.len(), 2);
assert!(
error_matcher
.module_patterns
.contains(&"internal*".to_string())
);
assert!(error_matcher.module_patterns.contains(&"test*".to_string()));
let complex_matcher = Match::info()
.module_pattern("app*")
.file_pattern("src/*.rs")
.span_pattern("database*")
.target_pattern("api*");
assert_eq!(complex_matcher.level, Level::INFO);
assert!(complex_matcher.include);
assert_eq!(complex_matcher.module_patterns.len(), 1);
assert_eq!(complex_matcher.file_patterns.len(), 1);
assert_eq!(complex_matcher.span_patterns.len(), 1);
assert_eq!(complex_matcher.target_patterns.len(), 1);
let all_matcher = Match::debug().all_modules();
assert_eq!(all_matcher.level, Level::DEBUG);
assert!(all_matcher.module_patterns.contains(&"*".to_string()));
let replaced_matcher = Match::warn()
.module_pattern("old_pattern")
.module_patterns(vec!["new_pattern1", "new_pattern2"]);
assert_eq!(replaced_matcher.module_patterns.len(), 2);
assert!(
!replaced_matcher
.module_patterns
.contains(&"old_pattern".to_string())
);
assert!(
replaced_matcher
.module_patterns
.contains(&"new_pattern1".to_string())
);
assert!(
replaced_matcher
.module_patterns
.contains(&"new_pattern2".to_string())
);
}
#[test]
fn test_pattern_matcher_optimization() {
let exact_matcher = PatternMatcher::new("exact_match");
assert!(exact_matcher.matches("exact_match"));
assert!(!exact_matcher.matches("not_exact"));
let wildcard_matcher = PatternMatcher::new("*");
assert!(wildcard_matcher.matches("anything"));
assert!(wildcard_matcher.matches(""));
let prefix_matcher = PatternMatcher::new("prefix*");
assert!(prefix_matcher.matches("prefix"));
assert!(prefix_matcher.matches("prefix_with_more"));
assert!(!prefix_matcher.matches("not_prefix"));
let suffix_matcher = PatternMatcher::new("*suffix");
assert!(suffix_matcher.matches("suffix"));
assert!(suffix_matcher.matches("with_suffix"));
assert!(!suffix_matcher.matches("suffix_not"));
let contains_matcher = PatternMatcher::new("*middle*");
assert!(contains_matcher.matches("middle"));
assert!(contains_matcher.matches("before_middle"));
assert!(contains_matcher.matches("middle_after"));
assert!(contains_matcher.matches("before_middle_after"));
assert!(!contains_matcher.matches("nomatch"));
let regex_matcher = PatternMatcher::new("test_*_end");
assert!(regex_matcher.matches("test_123_end"));
assert!(regex_matcher.matches("test_abc_end"));
assert!(!regex_matcher.matches("test_123_suffix"));
assert_eq!(matches("prefix*", "prefix_test"), prefix_matcher.matches("prefix_test"));
assert_eq!(matches("*suffix", "test_suffix"), suffix_matcher.matches("test_suffix"));
assert_eq!(matches("*middle*", "before_middle_after"), contains_matcher.matches("before_middle_after"));
}
#[test]
fn test_pattern_matcher_performance_categories() {
let patterns_optimized = vec![
("exact", "exact"),
("*", "anything"),
("prefix*", "prefix_test"),
("*suffix", "test_suffix"),
("*contains*", "test_contains_test"),
];
for (pattern, test_value) in patterns_optimized {
let matcher = PatternMatcher::new(pattern);
assert!(matcher.matches(test_value), "Pattern '{pattern}' should match '{test_value}'");
if let PatternMatcher::Regex(_) = matcher {
panic!("Pattern '{pattern}' should not use regex");
}
}
let patterns_regex = vec![
"prefix*suffix", "*middle*end", "start*middle*", "test_*_end", ];
for pattern in patterns_regex {
let matcher = PatternMatcher::new(pattern);
match matcher {
PatternMatcher::Regex(_) => {} _ => panic!("Pattern '{pattern}' should use regex"),
}
}
}