use matchy::database::QueryResult;
use matchy::{Database, DatabaseBuilder, DatabaseOptions};
use matchy_match_mode::MatchMode;
use std::collections::HashMap;
use tempfile::tempdir;
const NON_MATCHING_TEST_IP_RANGE: &str = "1.1.1.0/24";
fn is_match(result: &Option<QueryResult>) -> bool {
match result {
Some(QueryResult::NotFound) => false,
Some(_) => true,
None => false,
}
}
#[test]
fn test_stale_notfound_does_not_persist() {
let temp_dir = tempdir().expect("Failed to create temp dir");
let db_a_path = temp_dir.path().join("db_a_no_match.mxy");
let db_b_path = temp_dir.path().join("db_b_has_match.mxy");
let test_ip = "80.239.174.89";
{
let mut builder = DatabaseBuilder::new(MatchMode::CaseSensitive);
let mut data = HashMap::new();
data.insert(
"name".to_string(),
matchy_data_format::DataValue::String("other".to_string()),
);
builder.add_entry(NON_MATCHING_TEST_IP_RANGE, data).unwrap();
let bytes = builder.build().unwrap();
std::fs::write(&db_a_path, bytes).unwrap();
}
{
let mut builder = DatabaseBuilder::new(MatchMode::CaseSensitive);
let mut data = HashMap::new();
data.insert(
"name".to_string(),
matchy_data_format::DataValue::String("matched".to_string()),
);
builder.add_entry("80.239.0.0/16", data).unwrap();
let bytes = builder.build().unwrap();
std::fs::write(&db_b_path, bytes).unwrap();
}
{
let db_a = Database::open_with_options(DatabaseOptions {
path: db_a_path.clone(),
cache_capacity: Some(100), ..Default::default()
})
.expect("Failed to open DB_A");
let result_a = db_a.lookup(test_ip).expect("Lookup failed");
assert!(
!is_match(&result_a),
"DB_A should NOT match {test_ip}, but got: {result_a:?}"
);
println!("DB_A lookup for {test_ip}: {result_a:?} (expected NotFound)");
}
{
let db_b = Database::open_with_options(DatabaseOptions {
path: db_b_path.clone(),
cache_capacity: Some(100), ..Default::default()
})
.expect("Failed to open DB_B");
let result_b = db_b.lookup(test_ip).expect("Lookup failed");
assert!(
is_match(&result_b),
"BUG: DB_B SHOULD match {test_ip} but got {result_b:?} - stale NotFound from DB_A persisted!"
);
println!("DB_B lookup for {test_ip}: {result_b:?} (expected match)");
if let Some(QueryResult::Ip {
data, prefix_len, ..
}) = result_b
{
println!(" Matched with prefix_len={prefix_len}, data={data:?}");
}
}
println!("SUCCESS: Stale NotFound did NOT persist across database instances");
}
#[test]
fn test_stale_match_does_not_persist() {
let temp_dir = tempdir().expect("Failed to create temp dir");
let db_a_path = temp_dir.path().join("db_a_has_match.mxy");
let db_b_path = temp_dir.path().join("db_b_no_match.mxy");
let test_ip = "80.239.174.89";
{
let mut builder = DatabaseBuilder::new(MatchMode::CaseSensitive);
let mut data = HashMap::new();
data.insert(
"name".to_string(),
matchy_data_format::DataValue::String("matched".to_string()),
);
builder.add_entry("80.239.0.0/16", data).unwrap();
let bytes = builder.build().unwrap();
std::fs::write(&db_a_path, bytes).unwrap();
}
{
let mut builder = DatabaseBuilder::new(MatchMode::CaseSensitive);
let mut data = HashMap::new();
data.insert(
"name".to_string(),
matchy_data_format::DataValue::String("other".to_string()),
);
builder.add_entry(NON_MATCHING_TEST_IP_RANGE, data).unwrap();
let bytes = builder.build().unwrap();
std::fs::write(&db_b_path, bytes).unwrap();
}
{
let db_a = Database::open_with_options(DatabaseOptions {
path: db_a_path.clone(),
cache_capacity: Some(100), ..Default::default()
})
.expect("Failed to open DB_A");
let result_a = db_a.lookup(test_ip).expect("Lookup failed");
assert!(
is_match(&result_a),
"DB_A should match {test_ip}, but got: {result_a:?}"
);
println!("DB_A lookup for {test_ip}: {result_a:?} (expected match)");
}
{
let db_b = Database::open_with_options(DatabaseOptions {
path: db_b_path.clone(),
cache_capacity: Some(100), ..Default::default()
})
.expect("Failed to open DB_B");
let result_b = db_b.lookup(test_ip).expect("Lookup failed");
assert!(
!is_match(&result_b),
"BUG: DB_B should NOT match {test_ip} but got {result_b:?} - stale match from DB_A persisted!"
);
println!("DB_B lookup for {test_ip}: {result_b:?} (expected NotFound)");
}
println!("SUCCESS: Stale match did NOT persist across database instances");
}
#[test]
fn test_no_cache_works_correctly() {
let temp_dir = tempdir().expect("Failed to create temp dir");
let db_a_path = temp_dir.path().join("db_a_no_match.mxy");
let db_b_path = temp_dir.path().join("db_b_has_match.mxy");
let test_ip = "80.239.174.89";
{
let mut builder = DatabaseBuilder::new(MatchMode::CaseSensitive);
let mut data = HashMap::new();
data.insert(
"name".to_string(),
matchy_data_format::DataValue::String("other".to_string()),
);
builder.add_entry(NON_MATCHING_TEST_IP_RANGE, data).unwrap();
let bytes = builder.build().unwrap();
std::fs::write(&db_a_path, bytes).unwrap();
}
{
let mut builder = DatabaseBuilder::new(MatchMode::CaseSensitive);
let mut data = HashMap::new();
data.insert(
"name".to_string(),
matchy_data_format::DataValue::String("matched".to_string()),
);
builder.add_entry("80.239.0.0/16", data).unwrap();
let bytes = builder.build().unwrap();
std::fs::write(&db_b_path, bytes).unwrap();
}
{
let db_a = Database::open_with_options(DatabaseOptions {
path: db_a_path.clone(),
cache_capacity: Some(0), ..Default::default()
})
.expect("Failed to open DB_A");
let result_a = db_a.lookup(test_ip).expect("Lookup failed");
assert!(!is_match(&result_a), "DB_A should NOT match");
}
{
let db_b = Database::open_with_options(DatabaseOptions {
path: db_b_path.clone(),
cache_capacity: Some(0), ..Default::default()
})
.expect("Failed to open DB_B");
let result_b = db_b.lookup(test_ip).expect("Lookup failed");
assert!(
is_match(&result_b),
"DB_B should match when cache is disabled"
);
}
println!("SUCCESS: no_cache workaround works correctly");
}
#[test]
fn test_same_path_reload_gets_fresh_results() {
let temp_dir = tempdir().expect("Failed to create temp dir");
let db_path = temp_dir.path().join("reloadable.mxy");
let test_ip = "80.239.174.89";
{
let mut builder = DatabaseBuilder::new(MatchMode::CaseSensitive);
let mut data = HashMap::new();
data.insert(
"name".to_string(),
matchy_data_format::DataValue::String("initial".to_string()),
);
builder.add_entry(NON_MATCHING_TEST_IP_RANGE, data).unwrap();
let bytes = builder.build().unwrap();
std::fs::write(&db_path, bytes).unwrap();
}
{
let db = Database::open_with_options(DatabaseOptions {
path: db_path.clone(),
cache_capacity: Some(100),
..Default::default()
})
.expect("Failed to open DB");
let result = db.lookup(test_ip).expect("Lookup failed");
assert!(!is_match(&result), "Initial DB should NOT match");
println!("Initial lookup: {result:?}");
}
{
let mut builder = DatabaseBuilder::new(MatchMode::CaseSensitive);
let mut data = HashMap::new();
data.insert(
"name".to_string(),
matchy_data_format::DataValue::String("reloaded".to_string()),
);
builder.add_entry("80.239.0.0/16", data).unwrap();
let bytes = builder.build().unwrap();
std::fs::write(&db_path, bytes).unwrap();
}
{
let db = Database::open_with_options(DatabaseOptions {
path: db_path.clone(),
cache_capacity: Some(100),
..Default::default()
})
.expect("Failed to open reloaded DB");
let result = db.lookup(test_ip).expect("Lookup failed");
assert!(
is_match(&result),
"BUG: Reloaded DB should match {test_ip} but got {result:?} - stale cache from previous open!"
);
println!("Reloaded lookup: {result:?}");
}
println!("SUCCESS: Same path reload gets fresh results");
}