use super::*;
fn field(path: &str, value: &str) -> EventField {
EventField {
path: path.to_string(),
value: value.to_string(),
array_trail: vec![],
is_number: false,
}
}
#[test]
fn test_automaton_value_matcher_string() {
let mut matcher: AutomatonValueMatcher<String> = AutomatonValueMatcher::new();
matcher.add_string_match(b"hello", "p1".to_string());
matcher.add_string_match(b"world", "p2".to_string());
let matches = matcher.match_value(b"hello");
assert_eq!(matches.len(), 1);
assert!(matches.contains(&"p1".to_string()));
let matches = matcher.match_value(b"world");
assert_eq!(matches.len(), 1);
assert!(matches.contains(&"p2".to_string()));
let matches = matcher.match_value(b"foo");
assert!(matches.is_empty());
}
#[test]
fn test_automaton_value_matcher_prefix() {
let mut matcher: AutomatonValueMatcher<String> = AutomatonValueMatcher::new();
matcher.add_prefix_match(b"prod-", "p1".to_string());
matcher.add_prefix_match(b"test-", "p2".to_string());
let matches = matcher.match_value(b"prod-123");
assert_eq!(matches.len(), 1);
assert!(matches.contains(&"p1".to_string()));
let matches = matcher.match_value(b"test-abc");
assert_eq!(matches.len(), 1);
assert!(matches.contains(&"p2".to_string()));
let matches = matcher.match_value(b"dev-xyz");
assert!(matches.is_empty());
}
#[test]
fn test_automaton_value_matcher_shellstyle_single() {
let mut matcher: AutomatonValueMatcher<String> = AutomatonValueMatcher::new();
matcher.add_shellstyle_match(b"*.txt", "p1".to_string());
let matches = matcher.match_value(b"file.txt");
assert!(
matches.contains(&"p1".to_string()),
"file.txt should match *.txt"
);
let matches = matcher.match_value(b".txt");
assert!(
matches.contains(&"p1".to_string()),
".txt should match *.txt"
);
let matches = matcher.match_value(b"foo");
assert!(matches.is_empty(), "foo should not match *.txt");
}
#[test]
fn test_automaton_value_matcher_shellstyle_multiple() {
let mut matcher: AutomatonValueMatcher<String> = AutomatonValueMatcher::new();
matcher.add_shellstyle_match(b"*.txt", "p1".to_string());
matcher.add_shellstyle_match(b"test*", "p2".to_string());
let matches = matcher.match_value(b"random");
assert!(matches.is_empty(), "random should not match any pattern");
}
#[test]
fn test_automaton_value_matcher_mixed() {
let mut matcher: AutomatonValueMatcher<String> = AutomatonValueMatcher::new();
matcher.add_string_match(b"exact", "exact_match".to_string());
matcher.add_prefix_match(b"pre-", "prefix_match".to_string());
let matches = matcher.match_value(b"exact");
assert_eq!(matches.len(), 1);
assert!(matches.contains(&"exact_match".to_string()));
let matches = matcher.match_value(b"pre-fix");
assert_eq!(matches.len(), 1);
assert!(matches.contains(&"prefix_match".to_string()));
}
#[test]
fn test_arena_small_table_step() {
use arena::ArenaSmallTable;
let table = ArenaSmallTable::new();
for b in 0..BYTE_CEILING as u8 {
let (s, eps) = table.step(b);
assert!(
s.is_none(),
"byte {b} should have no transition in empty table"
);
assert!(
eps.is_empty(),
"byte {b} should have no epsilons in empty table"
);
}
}
#[test]
fn test_arena_small_table_with_mappings() {
use arena::{ArenaSmallTable, StateArena, StateId};
use std::sync::Arc;
let mut arena = StateArena::new();
let next_field = Arc::new(FieldMatcher::new());
let next_state = arena.alloc_with_table(ArenaSmallTable::new());
arena[next_state].field_transitions.push(next_field);
let table = ArenaSmallTable::with_mappings(StateId::NONE, b"ab", &[next_state, next_state]);
let (step_a, _) = table.step(b'a');
assert!(!step_a.is_none(), "byte 'a' should have a transition");
let (step_b, _) = table.step(b'b');
assert!(!step_b.is_none(), "byte 'b' should have a transition");
let (step_c, _) = table.step(b'c');
assert!(step_c.is_none(), "byte 'c' should have no transition");
}
#[test]
fn test_core_matcher_single_field_exact() {
use crate::json::Matcher;
let matcher: CoreMatcher<String> = CoreMatcher::new();
matcher
.add_pattern(
"p1".to_string(),
&[(
"status".to_string(),
vec![Matcher::Exact("active".to_string())],
)],
)
.unwrap();
let fields = vec![field("status", "active")];
let matches = matcher.matches_for_fields(&fields);
assert_eq!(matches.len(), 1);
assert!(matches.contains(&"p1".to_string()));
}
#[test]
fn test_core_matcher_single_field_no_match() {
use crate::json::Matcher;
let matcher: CoreMatcher<String> = CoreMatcher::new();
matcher
.add_pattern(
"p1".to_string(),
&[(
"status".to_string(),
vec![Matcher::Exact("active".to_string())],
)],
)
.unwrap();
let fields = vec![field("status", "inactive")];
let matches = matcher.matches_for_fields(&fields);
assert!(matches.is_empty());
}
#[test]
fn test_core_matcher_exists_true() {
use crate::json::Matcher;
let matcher: CoreMatcher<String> = CoreMatcher::new();
matcher
.add_pattern(
"p1".to_string(),
&[("name".to_string(), vec![Matcher::Exists(true)])],
)
.unwrap();
let fields = vec![field("name", "anything")];
let matches = matcher.matches_for_fields(&fields);
assert_eq!(
matches.len(),
1,
"exists:true should match when field exists"
);
}
#[test]
fn test_core_matcher_exists_false() {
use crate::json::Matcher;
let matcher: CoreMatcher<String> = CoreMatcher::new();
matcher
.add_pattern(
"p1".to_string(),
&[("name".to_string(), vec![Matcher::Exists(false)])],
)
.unwrap();
let fields = vec![field("other", "value")];
let matches = matcher.matches_for_fields(&fields);
assert_eq!(
matches.len(),
1,
"exists:false should match when field is absent"
);
}
#[test]
fn test_core_matcher_multi_field_and() {
use crate::json::Matcher;
let matcher: CoreMatcher<String> = CoreMatcher::new();
matcher
.add_pattern(
"p1".to_string(),
&[
(
"status".to_string(),
vec![Matcher::Exact("active".to_string())],
),
("type".to_string(), vec![Matcher::Exact("user".to_string())]),
],
)
.unwrap();
let fields = vec![field("status", "active"), field("type", "user")];
let matches = matcher.matches_for_fields(&fields);
assert_eq!(
matches.len(),
1,
"multi-field AND should match when all fields match"
);
}
#[test]
fn test_core_matcher_multi_field_partial_no_match() {
use crate::json::Matcher;
let matcher: CoreMatcher<String> = CoreMatcher::new();
matcher
.add_pattern(
"p1".to_string(),
&[
(
"status".to_string(),
vec![Matcher::Exact("active".to_string())],
),
("type".to_string(), vec![Matcher::Exact("user".to_string())]),
],
)
.unwrap();
let fields = vec![field("status", "active"), field("type", "admin")];
let matches = matcher.matches_for_fields(&fields);
assert!(
matches.is_empty(),
"multi-field AND should not match with partial field match"
);
}
#[test]
fn test_core_matcher_or_within_field() {
use crate::json::Matcher;
let matcher: CoreMatcher<String> = CoreMatcher::new();
matcher
.add_pattern(
"p1".to_string(),
&[(
"status".to_string(),
vec![
Matcher::Exact("active".to_string()),
Matcher::Exact("pending".to_string()),
],
)],
)
.unwrap();
let fields1 = vec![field("status", "active")];
let matches1 = matcher.matches_for_fields(&fields1);
assert_eq!(matches1.len(), 1, "OR within field should match 'active'");
let fields2 = vec![field("status", "pending")];
let matches2 = matcher.matches_for_fields(&fields2);
assert_eq!(matches2.len(), 1, "OR within field should match 'pending'");
let fields3 = vec![field("status", "completed")];
let matches3 = matcher.matches_for_fields(&fields3);
assert!(
matches3.is_empty(),
"OR within field should not match 'completed'"
);
}
#[test]
fn test_core_matcher_multiple_patterns() {
use crate::json::Matcher;
let matcher: CoreMatcher<String> = CoreMatcher::new();
matcher
.add_pattern(
"p1".to_string(),
&[(
"status".to_string(),
vec![Matcher::Exact("active".to_string())],
)],
)
.unwrap();
matcher
.add_pattern(
"p2".to_string(),
&[(
"status".to_string(),
vec![Matcher::Exact("pending".to_string())],
)],
)
.unwrap();
let fields = vec![field("status", "active")];
let matches = matcher.matches_for_fields(&fields);
assert_eq!(matches.len(), 1);
assert!(matches.contains(&"p1".to_string()));
}
#[test]
fn test_thread_safe_core_matcher_send_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<ThreadSafeCoreMatcher<String>>();
}
#[test]
fn test_thread_safe_core_matcher_single_field() {
use crate::json::Matcher;
let matcher: ThreadSafeCoreMatcher<String> = ThreadSafeCoreMatcher::new();
matcher
.add_pattern(
"p1".to_string(),
&[(
"status".to_string(),
vec![Matcher::Exact("active".to_string())],
)],
)
.unwrap();
let fields = vec![field("status", "active")];
let matches = matcher.matches_for_fields(&fields);
assert_eq!(matches.len(), 1);
assert!(matches.contains(&"p1".to_string()));
}
#[test]
fn test_thread_safe_core_matcher_no_match() {
use crate::json::Matcher;
let matcher: ThreadSafeCoreMatcher<String> = ThreadSafeCoreMatcher::new();
matcher
.add_pattern(
"p1".to_string(),
&[(
"status".to_string(),
vec![Matcher::Exact("active".to_string())],
)],
)
.unwrap();
let fields = vec![field("status", "inactive")];
let matches = matcher.matches_for_fields(&fields);
assert!(matches.is_empty());
}
#[test]
fn test_thread_safe_core_matcher_exists_true() {
use crate::json::Matcher;
let matcher: ThreadSafeCoreMatcher<String> = ThreadSafeCoreMatcher::new();
matcher
.add_pattern(
"p1".to_string(),
&[("name".to_string(), vec![Matcher::Exists(true)])],
)
.unwrap();
let fields = vec![field("name", "anything")];
let matches = matcher.matches_for_fields(&fields);
assert_eq!(
matches.len(),
1,
"exists:true should match when field exists"
);
}
#[test]
fn test_thread_safe_core_matcher_exists_false() {
use crate::json::Matcher;
let matcher: ThreadSafeCoreMatcher<String> = ThreadSafeCoreMatcher::new();
matcher
.add_pattern(
"p1".to_string(),
&[("name".to_string(), vec![Matcher::Exists(false)])],
)
.unwrap();
let fields = vec![field("other", "value")];
let matches = matcher.matches_for_fields(&fields);
assert_eq!(
matches.len(),
1,
"exists:false should match when field is absent"
);
}
#[test]
fn test_thread_safe_core_matcher_multiple_patterns() {
use crate::json::Matcher;
let matcher: ThreadSafeCoreMatcher<String> = ThreadSafeCoreMatcher::new();
matcher
.add_pattern(
"p1".to_string(),
&[(
"status".to_string(),
vec![Matcher::Exact("active".to_string())],
)],
)
.unwrap();
matcher
.add_pattern(
"p2".to_string(),
&[(
"status".to_string(),
vec![Matcher::Exact("pending".to_string())],
)],
)
.unwrap();
let fields = vec![field("status", "active")];
let matches = matcher.matches_for_fields(&fields);
assert_eq!(matches.len(), 1);
assert!(matches.contains(&"p1".to_string()));
}