use proptest::collection::vec as pvec;
use proptest::prelude::*;
use std::net::IpAddr;
use std::str::FromStr;
use std::sync::Arc;
use wirerust::*;
fn make_schema() -> FilterSchema {
FilterSchemaBuilder::new()
.field("http.method", FieldType::Bytes)
.field("port", FieldType::Int)
.field("tags", FieldType::Array(Box::new(FieldType::Bytes)))
.field("ip", FieldType::Ip)
.field("enabled", FieldType::Bool)
.field("status_code", FieldType::Int)
.field("user_agent", FieldType::Bytes)
.field("request_size", FieldType::Int)
.field("response_time", FieldType::Int)
.field("headers", FieldType::Array(Box::new(FieldType::Int)))
.build()
}
fn make_functions() -> FunctionRegistry {
let mut functions = FunctionRegistry::new();
register_builtins(&mut functions);
functions
}
#[test]
fn test_filter_matches() {
let schema = make_schema();
let functions = make_functions();
let filter_str = r#"http.method == "GET" && port in {80 443} && len(tags) == 2"#;
let expr = FilterParser::parse(filter_str, &schema).expect("parse");
let filter = CompiledFilter::new(expr, Arc::new(schema.clone()), Arc::new(functions.clone()));
let mut ctx = FilterContext::new();
ctx.set(
"http.method",
LiteralValue::Bytes(Arc::new(b"GET".to_vec()).into()),
&schema,
)
.unwrap();
ctx.set("port", LiteralValue::Int(80), &schema).unwrap();
ctx.set(
"tags",
LiteralValue::Array(
Arc::new(vec![
LiteralValue::Bytes(Arc::new(b"foo".to_vec()).into()),
LiteralValue::Bytes(Arc::new(b"bar".to_vec()).into()),
])
.into(),
),
&schema,
)
.unwrap();
assert!(filter.execute(&ctx).unwrap());
}
#[test]
fn test_filter_does_not_match() {
let schema = make_schema();
let functions = make_functions();
let filter_str = r#"http.method == "POST" || port == 22"#;
let expr = FilterParser::parse(filter_str, &schema).expect("parse");
let filter = CompiledFilter::new(expr, Arc::new(schema.clone()), Arc::new(functions.clone()));
let mut ctx = FilterContext::new();
ctx.set(
"http.method",
LiteralValue::Bytes(Arc::new(b"GET".to_vec()).into()),
&schema,
)
.unwrap();
ctx.set("port", LiteralValue::Int(80), &schema).unwrap();
assert!(!filter.execute(&ctx).unwrap());
}
#[test]
fn test_filter_with_function_call() {
let schema = make_schema();
let functions = make_functions();
let filter_str = r#"upper(http.method) == "GET""#;
let expr = FilterParser::parse(filter_str, &schema).expect("parse");
let filter = CompiledFilter::new(expr, Arc::new(schema.clone()), Arc::new(functions.clone()));
let mut ctx = FilterContext::new();
ctx.set(
"http.method",
LiteralValue::Bytes(Arc::new(b"get".to_vec()).into()),
&schema,
)
.unwrap();
ctx.set("port", LiteralValue::Int(80), &schema).unwrap();
ctx.set(
"tags",
LiteralValue::Array(
Arc::new(vec![
LiteralValue::Bytes(Arc::new(b"foo".to_vec()).into()),
LiteralValue::Bytes(Arc::new(b"bar".to_vec()).into()),
])
.into(),
),
&schema,
)
.unwrap();
assert!(filter.execute(&ctx).unwrap());
}
#[cfg(feature = "regex")]
#[test]
fn test_regex_matches() {
let schema = make_schema();
let functions = make_functions();
let filter_str = r#"user_agent matches "Mozilla.*Firefox""#;
let expr = FilterParser::parse(filter_str, &schema).expect("parse");
let filter = CompiledFilter::new(expr, Arc::new(schema.clone()), Arc::new(functions.clone()));
let mut ctx = FilterContext::new();
ctx.set(
"user_agent",
LiteralValue::Bytes(
Arc::new(
b"Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0".to_vec(),
)
.into(),
),
&schema,
)
.unwrap();
assert!(filter.execute(&ctx).unwrap());
}
#[cfg(feature = "regex")]
#[test]
fn test_regex_does_not_match() {
let schema = make_schema();
let functions = make_functions();
let filter_str = r#"user_agent matches "Chrome.*""#;
let expr = FilterParser::parse(filter_str, &schema).expect("parse");
let filter = CompiledFilter::new(expr, Arc::new(schema.clone()), Arc::new(functions.clone()));
let mut ctx = FilterContext::new();
ctx.set(
"user_agent",
LiteralValue::Bytes(
Arc::new(
b"Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0".to_vec(),
)
.into(),
),
&schema,
)
.unwrap();
assert!(!filter.execute(&ctx).unwrap());
}
#[cfg(feature = "regex")]
#[test]
fn test_regex_with_simple_pattern() {
let schema = make_schema();
let functions = make_functions();
let filter_str = r#"http.method matches "GET|POST""#;
let expr = FilterParser::parse(filter_str, &schema).expect("parse");
let filter = CompiledFilter::new(expr, Arc::new(schema.clone()), Arc::new(functions.clone()));
let mut ctx = FilterContext::new();
ctx.set(
"http.method",
LiteralValue::Bytes(Arc::new(b"POST".to_vec()).into()),
&schema,
)
.unwrap();
assert!(filter.execute(&ctx).unwrap());
}
#[test]
fn test_ip_address_equality() {
let schema = make_schema();
let functions = make_functions();
let filter_str = r#"ip == "192.168.1.1""#;
let expr = FilterParser::parse(filter_str, &schema).expect("parse");
let filter = CompiledFilter::new(expr, Arc::new(schema.clone()), Arc::new(functions.clone()));
let mut ctx = FilterContext::new();
let ip = IpAddr::from_str("192.168.1.1").unwrap();
ctx.set("ip", LiteralValue::Ip(ip), &schema).unwrap();
let _result = filter.execute(&ctx);
}
#[test]
fn test_ip_address_in_set() {
let schema = make_schema();
let functions = make_functions();
let filter_str = r#"ip in {"192.168.1.1" "10.0.0.1" "172.16.0.1"}"#;
let expr = FilterParser::parse(filter_str, &schema).expect("parse");
let filter = CompiledFilter::new(expr, Arc::new(schema.clone()), Arc::new(functions.clone()));
let mut ctx = FilterContext::new();
let ip = IpAddr::from_str("10.0.0.1").unwrap();
ctx.set("ip", LiteralValue::Ip(ip), &schema).unwrap();
let _result = filter.execute(&ctx);
}
#[test]
fn test_boolean_true() {
let schema = make_schema();
let functions = make_functions();
let filter_str = r#"enabled == true"#;
let expr = FilterParser::parse(filter_str, &schema).expect("parse");
let filter = CompiledFilter::new(expr, Arc::new(schema.clone()), Arc::new(functions.clone()));
let mut ctx = FilterContext::new();
ctx.set("enabled", LiteralValue::Bool(true), &schema)
.unwrap();
assert!(filter.execute(&ctx).unwrap());
}
#[test]
fn test_boolean_false() {
let schema = make_schema();
let functions = make_functions();
let filter_str = r#"enabled == false"#;
let expr = FilterParser::parse(filter_str, &schema).expect("parse");
let filter = CompiledFilter::new(expr, Arc::new(schema.clone()), Arc::new(functions.clone()));
let mut ctx = FilterContext::new();
ctx.set("enabled", LiteralValue::Bool(false), &schema)
.unwrap();
assert!(filter.execute(&ctx).unwrap());
}
#[test]
fn test_boolean_not() {
let schema = make_schema();
let functions = make_functions();
let filter_str = r#"not enabled"#;
let expr = FilterParser::parse(filter_str, &schema).expect("parse");
let filter = CompiledFilter::new(expr, Arc::new(schema.clone()), Arc::new(functions.clone()));
let mut ctx = FilterContext::new();
ctx.set("enabled", LiteralValue::Bool(false), &schema)
.unwrap();
assert!(filter.execute(&ctx).unwrap());
}
#[test]
fn test_simple_logical_operations() {
let schema = make_schema();
let functions = make_functions();
let filter_str = r#"http.method == "POST" && port == 443"#;
let expr = FilterParser::parse(filter_str, &schema).expect("parse");
println!("Simple logical operations parsed expression: {:#?}", expr);
let filter = CompiledFilter::new(expr, Arc::new(schema.clone()), Arc::new(functions.clone()));
let mut ctx = FilterContext::new();
ctx.set(
"http.method",
LiteralValue::Bytes(Arc::new(b"POST".to_vec()).into()),
&schema,
)
.unwrap();
ctx.set("port", LiteralValue::Int(443), &schema).unwrap();
let result = filter.execute(&ctx);
println!(
"Simple logical operations test result: {}",
result.as_ref().unwrap_or_else(|e| panic!("Error: {}", e))
);
assert!(result.as_ref().unwrap());
}
#[test]
fn test_parenthesized_expression() {
let schema = make_schema();
let functions = make_functions();
let filter_str = r#"(http.method == "POST") && (port == 443)"#;
let expr = FilterParser::parse(filter_str, &schema).expect("parse");
println!("Parenthesized expression parsed: {:#?}", expr);
let filter = CompiledFilter::new(expr, Arc::new(schema.clone()), Arc::new(functions.clone()));
let mut ctx = FilterContext::new();
ctx.set(
"http.method",
LiteralValue::Bytes(Arc::new(b"POST".to_vec()).into()),
&schema,
)
.unwrap();
ctx.set("port", LiteralValue::Int(443), &schema).unwrap();
let result = filter.execute(&ctx);
println!(
"Parenthesized expression test result: {}",
result.as_ref().unwrap_or_else(|e| panic!("Error: {}", e))
);
assert!(result.as_ref().unwrap());
}
#[test]
fn test_or_expression() {
let schema = make_schema();
let functions = make_functions();
let filter_str = r#"http.method == "GET" || http.method == "POST" || http.method == "PUT""#;
let expr = FilterParser::parse(filter_str, &schema).expect("parse");
println!("OR expression parsed: {:#?}", expr);
let filter = CompiledFilter::new(expr, Arc::new(schema.clone()), Arc::new(functions.clone()));
let mut ctx = FilterContext::new();
ctx.set(
"http.method",
LiteralValue::Bytes(Arc::new(b"POST".to_vec()).into()),
&schema,
)
.unwrap();
let result = filter.execute(&ctx);
println!(
"OR expression test result: {}",
result.as_ref().unwrap_or_else(|e| panic!("Error: {}", e))
);
assert!(result.as_ref().unwrap());
}
#[test]
fn test_mixed_and_or_expression() {
let schema = make_schema();
let functions = make_functions();
let filter_str =
r#"(http.method == "GET" || http.method == "POST") && (port == 80 || port == 443)"#;
let expr = FilterParser::parse(filter_str, &schema).expect("parse");
println!("Mixed AND/OR expression parsed: {:#?}", expr);
let filter = CompiledFilter::new(expr, Arc::new(schema.clone()), Arc::new(functions.clone()));
let mut ctx = FilterContext::new();
ctx.set(
"http.method",
LiteralValue::Bytes(Arc::new(b"POST".to_vec()).into()),
&schema,
)
.unwrap();
ctx.set("port", LiteralValue::Int(443), &schema).unwrap();
let result = filter.execute(&ctx);
println!(
"Mixed AND/OR expression test result: {}",
result.as_ref().unwrap_or_else(|e| panic!("Error: {}", e))
);
assert!(result.as_ref().unwrap());
}
#[test]
fn test_with_enabled_field() {
let schema = make_schema();
let functions = make_functions();
let filter_str = r#"enabled"#;
let expr = FilterParser::parse(filter_str, &schema).expect("parse");
println!("Enabled field expression parsed: {:#?}", expr);
let filter = CompiledFilter::new(expr, Arc::new(schema.clone()), Arc::new(functions.clone()));
let mut ctx = FilterContext::new();
ctx.set("enabled", LiteralValue::Bool(true), &schema)
.unwrap();
let result = filter.execute(&ctx);
println!(
"Enabled field test result: {}",
result.as_ref().unwrap_or_else(|e| panic!("Error: {}", e))
);
assert!(result.as_ref().unwrap());
}
#[test]
fn test_with_len_function() {
let schema = make_schema();
let functions = make_functions();
let filter_str = r#"len(headers) > 0"#;
let expr = FilterParser::parse(filter_str, &schema).expect("parse");
println!("Len function expression parsed: {:#?}", expr);
let filter = CompiledFilter::new(expr, Arc::new(schema.clone()), Arc::new(functions.clone()));
let mut ctx = FilterContext::new();
ctx.set(
"headers",
LiteralValue::Array(Arc::new(vec![LiteralValue::Int(100), LiteralValue::Int(200)]).into()),
&schema,
)
.unwrap();
let result = filter.execute(&ctx);
println!(
"Len function test result: {}",
result.as_ref().unwrap_or_else(|e| panic!("Error: {}", e))
);
assert!(result.as_ref().unwrap());
}
#[test]
fn test_complex_logical_operations() {
let schema = make_schema();
let functions = make_functions();
let filter_str = r#"(http.method == "GET" || http.method == "POST") && (port == 80 || port == 443) && enabled"#;
let expr = FilterParser::parse(filter_str, &schema).expect("parse");
println!("Parsed expression: {:#?}", expr);
let filter = CompiledFilter::new(expr, Arc::new(schema.clone()), Arc::new(functions.clone()));
let mut ctx = FilterContext::new();
ctx.set(
"http.method",
LiteralValue::Bytes(Arc::new(b"POST".to_vec()).into()),
&schema,
)
.unwrap();
ctx.set("port", LiteralValue::Int(443), &schema).unwrap();
ctx.set("enabled", LiteralValue::Bool(true), &schema)
.unwrap();
let result = filter.execute(&ctx);
println!(
"Complex logical operations test result: {}",
result.as_ref().unwrap_or_else(|e| panic!("Error: {}", e))
);
assert!(result.as_ref().unwrap());
}
#[test]
fn test_nested_parentheses() {
let schema = make_schema();
let functions = make_functions();
let filter_str = r#"((http.method == "GET") && (port in {80 443})) || ((http.method == "POST") && (port == 8080))"#;
let expr = FilterParser::parse(filter_str, &schema).expect("parse");
let filter = CompiledFilter::new(expr, Arc::new(schema.clone()), Arc::new(functions.clone()));
let mut ctx = FilterContext::new();
ctx.set(
"http.method",
LiteralValue::Bytes(Arc::new(b"POST".to_vec()).into()),
&schema,
)
.unwrap();
ctx.set("port", LiteralValue::Int(8080), &schema).unwrap();
assert!(filter.execute(&ctx).unwrap());
}
#[test]
fn test_numeric_comparisons() {
let schema = make_schema();
let functions = make_functions();
let filter_str = r#"status_code >= 200 && status_code < 300 && request_size > 1000"#;
let expr = FilterParser::parse(filter_str, &schema).expect("parse");
let filter = CompiledFilter::new(expr, Arc::new(schema.clone()), Arc::new(functions.clone()));
let mut ctx = FilterContext::new();
ctx.set("status_code", LiteralValue::Int(200), &schema)
.unwrap();
ctx.set("request_size", LiteralValue::Int(1500), &schema)
.unwrap();
assert!(filter.execute(&ctx).unwrap());
}
#[test]
fn test_not_in_operator() {
let schema = make_schema();
let functions = make_functions();
let filter_str = r#"port not in {22 25 110}"#;
let expr = FilterParser::parse(filter_str, &schema).expect("parse");
let filter = CompiledFilter::new(expr, Arc::new(schema.clone()), Arc::new(functions.clone()));
let mut ctx = FilterContext::new();
ctx.set("port", LiteralValue::Int(80), &schema).unwrap();
assert!(filter.execute(&ctx).unwrap());
}
#[test]
fn test_sum_function() {
let schema = make_schema();
let functions = make_functions();
let filter_str = r#"sum(headers) > 100"#;
let expr = FilterParser::parse(filter_str, &schema).expect("parse");
let filter = CompiledFilter::new(expr, Arc::new(schema.clone()), Arc::new(functions.clone()));
let mut ctx = FilterContext::new();
ctx.set(
"headers",
LiteralValue::Array(
Arc::new(vec![
LiteralValue::Int(50),
LiteralValue::Int(60),
LiteralValue::Int(70),
])
.into(),
),
&schema,
)
.unwrap();
assert!(filter.execute(&ctx).unwrap());
}
#[test]
fn test_multiple_function_calls() {
let schema = make_schema();
let functions = make_functions();
let filter_str = r#"len(tags) == 3 && sum(headers) == 180"#;
let expr = FilterParser::parse(filter_str, &schema).expect("parse");
let filter = CompiledFilter::new(expr, Arc::new(schema.clone()), Arc::new(functions.clone()));
let mut ctx = FilterContext::new();
ctx.set(
"tags",
LiteralValue::Array(
Arc::new(vec![
LiteralValue::Bytes(Arc::new(b"tag1".to_vec()).into()),
LiteralValue::Bytes(Arc::new(b"tag2".to_vec()).into()),
LiteralValue::Bytes(Arc::new(b"tag3".to_vec()).into()),
])
.into(),
),
&schema,
)
.unwrap();
ctx.set(
"headers",
LiteralValue::Array(
Arc::new(vec![
LiteralValue::Int(60),
LiteralValue::Int(60),
LiteralValue::Int(60),
])
.into(),
),
&schema,
)
.unwrap();
assert!(filter.execute(&ctx).unwrap());
}
#[test]
fn test_empty_array() {
let schema = make_schema();
let functions = make_functions();
let filter_str = r#"len(tags) == 0"#;
let expr = FilterParser::parse(filter_str, &schema).expect("parse");
let filter = CompiledFilter::new(expr, Arc::new(schema.clone()), Arc::new(functions.clone()));
let mut ctx = FilterContext::new();
ctx.set(
"tags",
LiteralValue::Array(Arc::new(vec![]).into()),
&schema,
)
.unwrap();
assert!(filter.execute(&ctx).unwrap());
}
#[test]
fn test_missing_field_returns_false() {
let schema = make_schema();
let functions = make_functions();
let filter_str = r#"http.method == "GET""#;
let expr = FilterParser::parse(filter_str, &schema).expect("parse");
let filter = CompiledFilter::new(expr, Arc::new(schema.clone()), Arc::new(functions.clone()));
let ctx = FilterContext::new();
assert!(!filter.execute(&ctx).unwrap());
}
#[test]
fn test_unknown_function_returns_false() {
let schema = make_schema();
let functions = make_functions();
let filter_str = r#"unknown_function(tags)"#;
let expr = FilterParser::parse(filter_str, &schema).expect("parse");
let filter = CompiledFilter::new(expr, Arc::new(schema.clone()), Arc::new(functions.clone()));
let mut ctx = FilterContext::new();
ctx.set(
"tags",
LiteralValue::Array(
Arc::new(vec![LiteralValue::Bytes(Arc::new(b"tag1".to_vec()).into())]).into(),
),
&schema,
)
.unwrap();
assert!(matches!(
filter.execute(&ctx),
Err(WirerustError::FunctionError(_))
));
}
#[test]
fn test_string_inequality() {
let schema = make_schema();
let functions = make_functions();
let filter_str = r#"http.method != "DELETE""#;
let expr = FilterParser::parse(filter_str, &schema).expect("parse");
let filter = CompiledFilter::new(expr, Arc::new(schema.clone()), Arc::new(functions.clone()));
let mut ctx = FilterContext::new();
ctx.set(
"http.method",
LiteralValue::Bytes(Arc::new(b"GET".to_vec()).into()),
&schema,
)
.unwrap();
assert!(filter.execute(&ctx).unwrap());
}
#[test]
fn test_case_insensitive_comparison() {
let schema = make_schema();
let functions = make_functions();
let filter_str = r#"upper(http.method) == "GET" && lower(http.method) == "get""#;
let expr = FilterParser::parse(filter_str, &schema).expect("parse");
let filter = CompiledFilter::new(expr, Arc::new(schema.clone()), Arc::new(functions.clone()));
let mut ctx = FilterContext::new();
ctx.set(
"http.method",
LiteralValue::Bytes(Arc::new(b"get".to_vec()).into()),
&schema,
)
.unwrap();
assert!(filter.execute(&ctx).unwrap());
}
#[test]
fn test_response_time_threshold() {
let schema = make_schema();
let functions = make_functions();
let filter_str = r#"response_time > 1000 && response_time <= 5000"#;
let expr = FilterParser::parse(filter_str, &schema).expect("parse");
let filter = CompiledFilter::new(expr, Arc::new(schema.clone()), Arc::new(functions.clone()));
let mut ctx = FilterContext::new();
ctx.set("response_time", LiteralValue::Int(2500), &schema)
.unwrap();
assert!(filter.execute(&ctx).unwrap());
}
#[test]
fn test_complex_web_request_filter() {
let schema = make_schema();
let functions = make_functions();
let filter_str = r#"
(http.method == "GET" || http.method == "POST") &&
(port == 80 || port == 443) &&
status_code >= 200 && status_code < 400 &&
response_time < 3000 &&
enabled &&
len(headers) > 0
"#;
let expr = FilterParser::parse(filter_str, &schema).expect("parse");
println!("Parsed expression: {:#?}", expr);
let filter = CompiledFilter::new(expr, Arc::new(schema.clone()), Arc::new(functions.clone()));
let mut ctx = FilterContext::new();
ctx.set(
"http.method",
LiteralValue::Bytes(Arc::new(b"GET".to_vec()).into()),
&schema,
)
.unwrap();
ctx.set("port", LiteralValue::Int(443), &schema).unwrap();
ctx.set("status_code", LiteralValue::Int(200), &schema)
.unwrap();
ctx.set("response_time", LiteralValue::Int(1500), &schema)
.unwrap();
ctx.set("enabled", LiteralValue::Bool(true), &schema)
.unwrap();
ctx.set(
"headers",
LiteralValue::Array(Arc::new(vec![LiteralValue::Int(100), LiteralValue::Int(200)]).into()),
&schema,
)
.unwrap();
let result = filter.execute(&ctx);
println!(
"Complex web request filter test result: {}",
result.as_ref().unwrap_or_else(|e| panic!("Error: {}", e))
);
assert!(result.as_ref().unwrap());
}
#[cfg(feature = "regex")]
#[test]
fn test_invalid_regex_pattern() {
let schema = make_schema();
let functions = make_functions();
let filter_str = r#"user_agent matches ".*""#; let expr = FilterParser::parse(filter_str, &schema).expect("parse");
let filter = CompiledFilter::new(expr, Arc::new(schema.clone()), Arc::new(functions.clone()));
let mut ctx = FilterContext::new();
ctx.set(
"user_agent",
LiteralValue::Bytes(Arc::new(b"test".to_vec()).into()),
&schema,
)
.unwrap();
assert!(filter.execute(&ctx).unwrap());
}
#[test]
fn test_type_mismatch_in_comparison() {
let schema = make_schema();
let functions = make_functions();
let filter_str = r#"port == "not_a_number""#;
let expr = FilterParser::parse(filter_str, &schema).expect("parse");
let filter = CompiledFilter::new(expr, Arc::new(schema.clone()), Arc::new(functions.clone()));
let mut ctx = FilterContext::new();
ctx.set("port", LiteralValue::Int(80), &schema).unwrap();
assert!(!filter.execute(&ctx).unwrap());
}
#[test]
fn test_boundary_values() {
let schema = make_schema();
let functions = make_functions();
let filter_str = r#"status_code >= 0 && status_code <= 999 && port > 0 && port < 65536"#;
let expr = FilterParser::parse(filter_str, &schema).expect("parse");
let filter = CompiledFilter::new(expr, Arc::new(schema.clone()), Arc::new(functions.clone()));
let mut ctx = FilterContext::new();
ctx.set("status_code", LiteralValue::Int(0), &schema)
.unwrap();
ctx.set("port", LiteralValue::Int(1), &schema).unwrap();
assert!(filter.execute(&ctx).unwrap());
let mut ctx2 = FilterContext::new();
ctx2.set("status_code", LiteralValue::Int(999), &schema)
.unwrap();
ctx2.set("port", LiteralValue::Int(65535), &schema).unwrap();
assert!(filter.execute(&ctx2).unwrap());
}
#[test]
fn test_mixed_operators() {
let schema = make_schema();
let functions = make_functions();
let filter_str = r#"http.method == "GET" && port != 22 && status_code in {200 201 204} && response_time <= 1000"#;
let expr = FilterParser::parse(filter_str, &schema).expect("parse");
let filter = CompiledFilter::new(expr, Arc::new(schema.clone()), Arc::new(functions.clone()));
let mut ctx = FilterContext::new();
ctx.set(
"http.method",
LiteralValue::Bytes(Arc::new(b"GET".to_vec()).into()),
&schema,
)
.unwrap();
ctx.set("port", LiteralValue::Int(80), &schema).unwrap();
ctx.set("status_code", LiteralValue::Int(201), &schema)
.unwrap();
ctx.set("response_time", LiteralValue::Int(500), &schema)
.unwrap();
assert!(filter.execute(&ctx).unwrap());
}
proptest! {
#[test]
fn parser_does_not_panic_on_random_input(s in ".{0,256}") {
let schema = make_schema();
let _ = FilterParser::parse(&s, &schema);
}
}
proptest! {
#[test]
fn parse_roundtrip_simple_int(val in 0i64..10000) {
let schema = make_schema();
let expr_str = format!("port == {}", val);
let expr = FilterParser::parse(&expr_str, &schema).expect("parse");
let _ = format!("{:?}", expr); }
}
proptest! {
#[test]
fn prop_type_inference_array_infers_type(vals in pvec(0i64..100, 0..10)) {
use wirerust::{LiteralValue, FieldType};
let arr = LiteralValue::Array(Arc::new(vals.iter().map(|&i| LiteralValue::Int(i)).collect::<Vec<_>>()).into());
let ty = arr.get_type();
if vals.is_empty() {
assert_eq!(ty, FieldType::Array(Box::new(FieldType::Unknown)));
} else {
assert_eq!(ty, FieldType::Array(Box::new(FieldType::Int)));
}
}
#[test]
fn prop_type_inference_array_mixed_types(ints in pvec(0i64..100, 0..5), strs in pvec(".*", 0..5)) {
use wirerust::{LiteralValue, FieldType};
let mut vals: Vec<LiteralValue> = ints.iter().map(|&i| LiteralValue::Int(i)).collect();
vals.extend(strs.iter().map(|s| LiteralValue::Bytes(Arc::new(s.as_bytes().to_vec()).into())));
let arr = LiteralValue::Array(Arc::new(vals).into());
let ty = arr.get_type();
if ints.is_empty() || strs.is_empty() {
if ints.is_empty() && !strs.is_empty() {
assert_eq!(ty, FieldType::Array(Box::new(FieldType::Bytes)));
} else if !ints.is_empty() && strs.is_empty() {
assert_eq!(ty, FieldType::Array(Box::new(FieldType::Int)));
} else {
assert_eq!(ty, FieldType::Array(Box::new(FieldType::Unknown)));
}
} else {
assert_eq!(ty, FieldType::Array(Box::new(FieldType::Unknown)));
}
}
#[test]
fn prop_type_inference_map_infers_type(ints in pvec(0i64..100, 0..5), strs in pvec(".*", 0..5)) {
use wirerust::{LiteralValue, FieldType};
use std::collections::HashMap;
let mut map = HashMap::new();
for (i, v) in ints.iter().enumerate() {
map.insert(format!("k_int_{}", i), LiteralValue::Int(*v));
}
for (i, s) in strs.iter().enumerate() {
map.insert(format!("k_str_{}", i), LiteralValue::Bytes(Arc::new(s.as_bytes().to_vec()).into()));
}
let val = LiteralValue::Map(Arc::new(map).into());
let ty = val.get_type();
if ints.is_empty() || strs.is_empty() {
if ints.is_empty() && !strs.is_empty() {
assert_eq!(ty, FieldType::Map(Box::new(FieldType::Bytes)));
} else if !ints.is_empty() && strs.is_empty() {
assert_eq!(ty, FieldType::Map(Box::new(FieldType::Int)));
} else {
assert_eq!(ty, FieldType::Map(Box::new(FieldType::Unknown)));
}
} else {
assert_eq!(ty, FieldType::Map(Box::new(FieldType::Unknown)));
}
}
}
proptest! {
#[test]
fn prop_execute_random_filter_and_context(int_val in 0i64..100, str_val in ".{0,16}") {
use wirerust::*;
let _schema = FilterSchemaBuilder::new()
.field("foo", FieldType::Int)
.field("bar", FieldType::Bytes)
.build();
let engine = WirerustEngineBuilder::new()
.field("foo", FieldType::Int)
.field("bar", FieldType::Bytes)
.build();
let filter_str = if int_val % 2 == 0 {
format!("foo == {}", int_val)
} else {
format!("bar == \"{}\"", str_val)
};
let filter = engine.parse_and_compile(&filter_str);
let mut ctx = FilterContext::new();
ctx.set_int("foo", int_val, &engine.schema());
ctx.set_bytes("bar", str_val.as_bytes(), &engine.schema());
if let Ok(filter) = filter {
let _ = engine.execute(&filter, &ctx);
}
}
}
#[test]
fn test_error_reporting_unknown_function() {
use wirerust::*;
let _schema = FilterSchemaBuilder::new()
.field("foo", FieldType::Int)
.build();
let engine = WirerustEngineBuilder::new()
.field("foo", FieldType::Int)
.build();
let filter = engine.parse_and_compile("not_a_function(foo)").unwrap();
let mut ctx = FilterContext::new();
ctx.set_int("foo", 1, &engine.schema());
let result = engine.execute(&filter, &ctx);
assert!(matches!(result, Err(WirerustError::FunctionError(_))));
}
#[test]
fn test_error_reporting_type_mismatch() {
use wirerust::*;
let _schema = FilterSchemaBuilder::new()
.field("foo", FieldType::Int)
.build();
let engine = WirerustEngineBuilder::new()
.field("foo", FieldType::Int)
.build();
let filter = engine.parse_and_compile("foo == \"not_an_int\"").unwrap();
let mut ctx = FilterContext::new();
ctx.set_int("foo", 1, &engine.schema());
let result = engine.execute(&filter, &ctx);
assert!(matches!(result, Ok(false)));
}