use super::sparse_set::SparseSet;
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 pattern_ids = matcher.match_value(b"hello");
assert_eq!(pattern_ids.len(), 1);
assert!(pattern_ids.contains(&"p1".to_string()));
let pattern_ids = matcher.match_value(b"world");
assert_eq!(pattern_ids.len(), 1);
assert!(pattern_ids.contains(&"p2".to_string()));
let pattern_ids = matcher.match_value(b"foo");
assert!(pattern_ids.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 pattern_ids = matcher.match_value(b"prod-123");
assert_eq!(pattern_ids.len(), 1);
assert!(pattern_ids.contains(&"p1".to_string()));
let pattern_ids = matcher.match_value(b"test-abc");
assert_eq!(pattern_ids.len(), 1);
assert!(pattern_ids.contains(&"p2".to_string()));
let pattern_ids = matcher.match_value(b"dev-xyz");
assert!(pattern_ids.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 pattern_ids = matcher.match_value(b"file.txt");
assert!(
pattern_ids.contains(&"p1".to_string()),
"file.txt should match *.txt"
);
let pattern_ids = matcher.match_value(b".txt");
assert!(
pattern_ids.contains(&"p1".to_string()),
".txt should match *.txt"
);
let pattern_ids = matcher.match_value(b"foo");
assert!(pattern_ids.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 pattern_ids = matcher.match_value(b"random");
assert!(
pattern_ids.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 pattern_ids = matcher.match_value(b"exact");
assert_eq!(pattern_ids.len(), 1);
assert!(pattern_ids.contains(&"exact_match".to_string()));
let pattern_ids = matcher.match_value(b"pre-fix");
assert_eq!(pattern_ids.len(), 1);
assert!(pattern_ids.contains(&"prefix_match".to_string()));
}
#[test]
fn test_arena_small_table_step() {
use arena::SmallTable;
let table = SmallTable::new();
for b in 0..BYTE_CEILING_U8 {
assert!(
table.dstep(b).is_none(),
"byte {b} should have no transition in empty table"
);
}
assert!(
table.epsilons.is_empty(),
"empty table should have no epsilons"
);
}
#[test]
fn test_arena_small_table_with_mappings() {
use arena::{SmallTable, 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(SmallTable::new());
arena[next_state].field_transitions.push(next_field);
let table = SmallTable::with_mappings(StateId::NONE, b"ab", &[next_state, next_state]);
assert!(
!table.dstep(b'a').is_none(),
"byte 'a' should have a transition"
);
assert!(
!table.dstep(b'b').is_none(),
"byte 'b' should have a transition"
);
assert!(
table.dstep(b'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 pattern_ids = matcher.matches_for_fields(&fields);
assert_eq!(pattern_ids.len(), 1);
assert!(pattern_ids.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 pattern_ids = matcher.matches_for_fields(&fields);
assert!(pattern_ids.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 pattern_ids = matcher.matches_for_fields(&fields);
assert_eq!(
pattern_ids.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 pattern_ids = matcher.matches_for_fields(&fields);
assert_eq!(
pattern_ids.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 pattern_ids = matcher.matches_for_fields(&fields);
assert_eq!(
pattern_ids.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 pattern_ids = matcher.matches_for_fields(&fields);
assert!(
pattern_ids.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 pattern_ids = matcher.matches_for_fields(&fields);
assert_eq!(pattern_ids.len(), 1);
assert!(pattern_ids.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 pattern_ids = matcher.matches_for_fields(&fields);
assert_eq!(pattern_ids.len(), 1);
assert!(pattern_ids.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 pattern_ids = matcher.matches_for_fields(&fields);
assert!(pattern_ids.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 pattern_ids = matcher.matches_for_fields(&fields);
assert_eq!(
pattern_ids.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 pattern_ids = matcher.matches_for_fields(&fields);
assert_eq!(
pattern_ids.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 pattern_ids = matcher.matches_for_fields(&fields);
assert_eq!(pattern_ids.len(), 1);
assert!(pattern_ids.contains(&"p1".to_string()));
}
#[test]
fn test_nfa_buffers_clear() {
let mut bufs = small_table::NfaBuffers::new();
bufs.arena_bufs
.current_states
.push(arena::StateId::from_index(1));
bufs.arena_bufs
.next_states
.push(arena::StateId::from_index(2));
bufs.arena_bufs.transitions.push(42);
bufs.clear();
assert!(bufs.arena_bufs.current_states.is_empty());
assert!(bufs.arena_bufs.next_states.is_empty());
assert!(bufs.arena_bufs.transitions.is_empty());
}
#[test]
fn test_arena_nfa_bufs_clear_observable_through_matching() {
use crate::json::Matcher;
let matcher: CoreMatcher<String> = CoreMatcher::new();
matcher
.add_pattern(
"p1".to_string(),
&[(
"color".to_string(),
vec![Matcher::Shellstyle("r*".to_string())],
)],
)
.unwrap();
let fields1 = vec![field("color", "\"red\"")];
let matches1 = matcher.matches_for_fields(&fields1);
assert_eq!(matches1.len(), 1);
assert!(matches1.contains(&"p1".to_string()));
let fields2 = vec![field("color", "\"blue\"")];
let matches2 = matcher.matches_for_fields(&fields2);
assert!(
matches2.is_empty(),
"stale arena NFA buffers should not cause false pattern_ids"
);
}
#[test]
fn test_field_matcher_with_match_id_stores_id() {
let fm = small_table::FieldMatcher::with_match_id(42);
assert_eq!(
fm.match_id,
Some(42),
"with_match_id(42) must set match_id to Some(42)"
);
}
#[test]
fn test_field_matcher_with_match_id_vs_default() {
let fm_with_id = small_table::FieldMatcher::with_match_id(99);
let fm_default = small_table::FieldMatcher::new();
assert_eq!(fm_with_id.match_id, Some(99));
assert_eq!(fm_default.match_id, None);
}
#[test]
fn test_sparse_set_capacity_returns_correct_value() {
let set = SparseSet::new(5);
assert_eq!(set.capacity(), 5);
let set2 = SparseSet::new(2);
assert_eq!(set2.capacity(), 2);
}
#[test]
fn test_sparse_set_insert_return_value() {
let mut set = SparseSet::new(10);
assert!(set.insert(3));
assert!(!set.insert(3));
}
#[test]
fn test_sparse_set_insert_increments_len() {
let mut set = SparseSet::new(10);
set.insert(5);
assert!(set.contains(5));
set.insert(6);
assert!(set.contains(5));
assert!(set.contains(6));
set.insert(7);
assert!(set.contains(5));
assert!(set.contains(6));
assert!(set.contains(7));
}
#[test]
fn test_sparse_set_contains_correctness() {
let mut set = SparseSet::new(10);
assert!(!set.contains(0));
assert!(!set.contains(5));
set.insert(5);
assert!(set.contains(5));
assert!(!set.contains(0));
}
#[test]
fn test_sparse_set_contains_rejects_uninserted_id() {
let mut set = SparseSet::new(10);
set.insert(3);
assert!(!set.contains(0));
}
#[test]
fn test_sparse_set_contains_after_multiple_inserts() {
let mut set = SparseSet::new(10);
set.insert(3);
set.insert(7);
assert!(set.contains(3));
assert!(set.contains(7));
}
#[test]
fn test_sparse_set_contains_single_element() {
let mut set = SparseSet::new(10);
set.insert(3);
assert!(set.contains(3));
}
#[test]
fn test_sparse_set_no_stale_entries_after_clear() {
let mut set = SparseSet::new(10);
set.insert(3);
set.insert(5);
set.clear();
set.insert(8);
assert!(!set.contains(5));
}
#[test]
fn test_sparse_set_contains_cross_check() {
let mut set = SparseSet::new(10);
set.insert(4);
assert!(set.contains(4));
assert!(!set.contains(0));
}
#[test]
fn test_sparse_set_clear_is_effective() {
let mut set = SparseSet::new(10);
set.insert(2);
set.insert(4);
set.insert(6);
set.clear();
assert!(!set.contains(2));
assert!(!set.contains(4));
assert!(!set.contains(6));
assert!(set.insert(2));
}
#[test]
fn test_sparse_set_insert_after_clear_correctness() {
let mut set = SparseSet::new(10);
set.insert(1);
set.insert(2);
set.insert(3);
set.clear();
assert!(set.insert(4));
assert!(set.contains(4));
assert!(!set.contains(1));
assert!(set.insert(1));
assert!(set.contains(1));
assert!(set.contains(4));
}
#[test]
fn test_sparse_set_non_zero_ids_only() {
let mut set = SparseSet::new(20);
set.insert(15);
set.insert(10);
set.insert(18);
assert!(set.contains(15));
assert!(set.contains(10));
assert!(set.contains(18));
assert!(!set.contains(0));
assert!(!set.contains(1));
assert!(!set.contains(5));
assert!(!set.contains(19));
}
#[test]
fn test_sparse_set_fill_to_capacity() {
let cap = 8;
let mut set = SparseSet::new(cap);
for i in 0..cap {
assert!(set.insert(i), "insert({i}) should return true");
}
for i in 0..cap {
assert!(set.contains(i));
assert!(!set.insert(i), "duplicate insert({i}) should return false");
}
assert_eq!(set.capacity(), cap);
}