#![cfg(feature = "bloom")]
mod support;
use crate::support::*;
use assert_matches::assert_matches;
use redis::bloom::{
BloomFilterDumpChunk, BloomFilterDumpIterator, BloomFilterInfoType, BloomFilterInsertOptions,
BloomFilterScalingOptions,
};
use redis::{TypedCommands, ValueType};
use redis_test::server::Module;
use std::vec;
const KEY_1: &str = "test_bloom_1";
const KEY_2: &str = "test_bloom_2";
const KEY_3: &str = "test_bloom_3";
#[test]
fn test_module_bloom_single_value_updates() {
let ctx = TestContext::with_modules(&[Module::Bloom]);
let mut con = ctx.connection();
assert_eq!(con.bf_add(KEY_1, "foo"), Ok(true));
assert_eq!(
con.bf_mexists(KEY_1, &["foo", "bar", "baz"]),
Ok(vec![true, false, false])
);
assert_eq!(con.bf_add(KEY_1, "bar"), Ok(true));
assert_eq!(
con.bf_mexists(KEY_1, &["foo", "bar", "baz"]),
Ok(vec![true, true, false])
);
assert_eq!(con.bf_add(KEY_1, "foo"), Ok(false));
assert_eq!(
con.bf_mexists(KEY_1, &["foo", "bar", "baz"]),
Ok(vec![true, true, false])
);
assert_eq!(con.set(KEY_2, "quux"), Ok(()));
assert_eq!(
con.bf_add(KEY_2, "foo").unwrap_err().code(),
Some("WRONGTYPE")
);
assert_eq!(con.get(KEY_2), Ok(Some("quux".to_string())));
}
#[test]
fn test_module_bloom_multiple_value_updates() {
let ctx = TestContext::with_modules(&[Module::Bloom]);
let mut con = ctx.connection();
assert_eq!(
con.bf_madd(KEY_1, &["foo", "foo", "bar"]),
Ok(vec![true, false, true])
);
assert_eq!(con.bf_mexists(KEY_1, &["foo", "bar"]), Ok(vec![true, true]));
assert_eq!(con.bf_insert(KEY_1, &["bar", "baz"]), Ok(vec![false, true]));
assert_eq!(
con.bf_mexists(KEY_1, &["foo", "bar", "baz"]),
Ok(vec![true, true, true])
);
let options = BloomFilterInsertOptions::default().nocreate();
assert_matches!(con.bf_insert_options(KEY_2, &["foo"], options).unwrap_err().detail(), Some(d) if d.contains("not found"));
let options = BloomFilterInsertOptions::default()
.capacity(2)
.expansion(BloomFilterScalingOptions::NonScaling);
assert_eq!(
con.bf_insert_options(KEY_3, &["foo", "bar"], options),
Ok(vec![true, true])
);
assert_matches!(con.bf_madd(KEY_3, &["baz"]).unwrap_err().detail(), Some(d) if d.contains("full"));
assert_eq!(
con.bf_mexists(KEY_3, &["foo", "bar", "baz"]),
Ok(vec![true, true, false])
);
}
#[test]
fn test_module_bloom_infos() {
let ctx = TestContext::with_modules(&[Module::Bloom]);
let mut con = ctx.connection();
assert_eq!(con.bf_card(KEY_1), Ok(0));
assert_eq!(con.key_type(KEY_1), Ok(ValueType::None));
assert_matches!(con.bf_info(KEY_1).unwrap_err().detail(), Some(d) if d.contains("not found"));
assert_matches!(con.bf_info_type(KEY_1, BloomFilterInfoType::Items).unwrap_err().detail(), Some(d) if d.contains("not found"));
assert_eq!(con.bf_exists(KEY_1, "foo"), Ok(false));
assert_eq!(
con.bf_mexists(KEY_1, &["foo", "bar", "baz"]),
Ok(vec![false, false, false])
);
assert_eq!(con.bf_add(KEY_1, "foo"), Ok(true));
assert_eq!(con.bf_card(KEY_1), Ok(1));
let bf_type = con.key_type(KEY_1).unwrap();
assert!([ValueType::BloomFilterRedis, ValueType::BloomFilterValKey].contains(&bf_type));
assert_eq!(
con.bf_info(KEY_1)
.unwrap()
.get("Number of items inserted")
.unwrap(),
&1.
);
assert_eq!(
con.bf_info_type(KEY_1, BloomFilterInfoType::Items).unwrap(),
1.
);
assert_eq!(con.bf_exists(KEY_1, "foo"), Ok(true));
assert_eq!(con.bf_exists(KEY_1, "bar"), Ok(false));
assert_eq!(
con.bf_mexists(KEY_1, &["foo", "bar", "baz"]),
Ok(vec![true, false, false])
);
assert_eq!(con.bf_add(KEY_1, "bar"), Ok(true));
assert_eq!(con.bf_card(KEY_1), Ok(2));
assert_eq!(con.key_type(KEY_1), Ok(bf_type.clone()));
assert_eq!(
con.bf_info(KEY_1)
.unwrap()
.get("Number of items inserted")
.unwrap(),
&2.
);
assert_eq!(
con.bf_info_type(KEY_1, BloomFilterInfoType::Items).unwrap(),
2.
);
assert_eq!(con.bf_exists(KEY_1, "foo"), Ok(true));
assert_eq!(con.bf_exists(KEY_1, "bar"), Ok(true));
assert_eq!(con.bf_exists(KEY_1, "baz"), Ok(false));
assert_eq!(
con.bf_mexists(KEY_1, &["foo", "bar", "baz"]),
Ok(vec![true, true, false])
);
assert_eq!(con.bf_add(KEY_1, "foo"), Ok(false));
assert_eq!(con.bf_card(KEY_1), Ok(2));
assert_eq!(con.key_type(KEY_1), Ok(bf_type.clone()));
assert_eq!(
con.bf_info(KEY_1)
.unwrap()
.get("Number of items inserted")
.unwrap(),
&2.
);
assert_eq!(
con.bf_info_type(KEY_1, BloomFilterInfoType::Items).unwrap(),
2.
);
assert_eq!(con.bf_exists(KEY_1, "foo"), Ok(true));
assert_eq!(con.bf_exists(KEY_1, "bar"), Ok(true));
assert_eq!(con.bf_exists(KEY_1, "baz"), Ok(false));
assert_eq!(
con.bf_mexists(KEY_1, &["foo", "bar", "baz"]),
Ok(vec![true, true, false])
);
assert_eq!(con.set(KEY_2, "quux"), Ok(()));
assert_eq!(con.bf_card(KEY_2).unwrap_err().code(), Some("WRONGTYPE"));
assert_eq!(con.bf_info(KEY_2).unwrap_err().code(), Some("WRONGTYPE"));
assert_eq!(
con.bf_info_type(KEY_2, BloomFilterInfoType::Items)
.unwrap_err()
.code(),
Some("WRONGTYPE")
);
assert_eq!(con.bf_exists(KEY_2, "foo"), Ok(false));
assert_eq!(
con.bf_mexists(KEY_2, &["foo", "bar", "baz"]),
Ok(vec![false, false, false])
);
assert_eq!(con.get(KEY_2), Ok(Some("quux".to_string())));
}
#[test]
fn test_module_bloom_reserving() {
let ctx = TestContext::with_modules(&[Module::Bloom]);
let mut con = ctx.connection();
assert_eq!(con.bf_reserve(KEY_1, 0.4711, 42), Ok(()));
assert_eq!(
con.bf_info_type(KEY_1, BloomFilterInfoType::Capacity)
.unwrap(),
42.
);
let options = BloomFilterScalingOptions::ExpansionRate(23);
assert_eq!(con.bf_reserve_options(KEY_2, 0.4711, 42, options), Ok(()));
assert_eq!(
con.bf_info_type(KEY_2, BloomFilterInfoType::Capacity)
.unwrap(),
42.
);
assert_eq!(
con.bf_info_type(KEY_2, BloomFilterInfoType::Expansion)
.unwrap(),
23.
);
assert_matches!(con.bf_reserve(KEY_2, 0.4711, 42).unwrap_err().detail(), Some(d) if d.contains("exists"));
assert_eq!(con.set(KEY_3, "quux"), Ok(()));
assert_eq!(
con.bf_reserve(KEY_3, 0.4711, 42).unwrap_err().code(),
Some("WRONGTYPE")
);
assert_eq!(con.get(KEY_3), Ok(Some("quux".to_string())));
}
#[test]
fn test_module_bloom_dump_and_load() {
let ctx = TestContext::with_modules(&[Module::Bloom]);
let mut con = ctx.connection();
let options = BloomFilterInsertOptions::default()
.capacity(42)
.error_rate(0.004711)
.expansion(BloomFilterScalingOptions::ExpansionRate(23));
assert_eq!(
con.bf_insert_options(KEY_1, &["foo", "bar"], options),
Ok(vec![true, true])
);
let original_info = con.bf_info(KEY_1).unwrap();
let mut iterator: i64 = 0;
let mut chunks: Vec<BloomFilterDumpChunk> = Vec::new();
loop {
let dump = con.bf_scandump(KEY_1, iterator).unwrap();
if dump.iterator == 0 {
break;
}
iterator = dump.iterator;
chunks.push(dump);
}
let deleted = con.del(KEY_1).unwrap();
assert_eq!(deleted, 1);
for chunk in chunks {
con.bf_loadchunk(KEY_2, chunk).unwrap();
}
let results: Vec<bool> = con.bf_mexists(KEY_2, &["foo", "bar"]).unwrap();
assert_eq!(results, vec![true, true]);
let loaded_info = con.bf_info(KEY_2).unwrap();
assert_eq!(loaded_info, original_info);
}
#[test]
fn test_module_bloom_dump_iterator() {
let ctx = TestContext::with_modules(&[Module::Bloom]);
let mut con = ctx.connection();
let options = BloomFilterInsertOptions::default()
.capacity(42)
.error_rate(0.004711)
.expansion(BloomFilterScalingOptions::ExpansionRate(23));
assert_eq!(
con.bf_insert_options(KEY_1, &["foo", "bar"], options),
Ok(vec![true, true])
);
let original_info = con.bf_info(KEY_1).unwrap();
let dump_iterator = BloomFilterDumpIterator::new(&mut con, KEY_1);
let chunks = dump_iterator.map(|r| r.unwrap()).collect::<Vec<_>>();
let deleted = con.del(KEY_1).unwrap();
assert_eq!(deleted, 1);
for chunk in chunks {
con.bf_loadchunk(KEY_2, chunk).unwrap();
}
let results: Vec<bool> = con.bf_mexists(KEY_2, &["foo", "bar"]).unwrap();
assert_eq!(results, vec![true, true]);
let loaded_info = con.bf_info(KEY_2).unwrap();
assert_eq!(loaded_info, original_info);
}