use wedb_embed::{Conf, ScopeId, WeDb, decode_u64_varint, encode_u64_varint};
#[test]
fn test_keyspace_isolated_keyspaces() {
let dir = tempfile::tempdir().expect("create tempdir failed");
let conf = Conf::new(dir.path().to_str().unwrap());
let db = WeDb::open_with_conf(&conf).expect("open db failed");
db.data.insert(b"k1", b"v1").expect("data insert failed");
assert_eq!(db.data.get(b"k1").unwrap().unwrap().as_ref(), b"v1");
db.meta
.insert(b"meta:1", b"m1")
.expect("meta insert failed");
assert_eq!(db.meta.get(b"meta:1").unwrap().unwrap().as_ref(), b"m1");
assert!(db.data.get(b"meta:1").unwrap().is_none());
}
#[test]
fn test_oppv_monotonic_and_boundaries() {
let test_cases = [
0u64,
1,
127,
128,
1000,
16511,
16512,
100_000,
2113663,
2113664,
10_000_000,
270549119,
270549120,
1_000_000_000,
u32::MAX as u64,
(u32::MAX as u64) + 1,
1_000_000_000_000,
1_000_000_000_000_000,
u64::MAX - 100,
u64::MAX,
];
let mut prev_encoded = Vec::new();
let mut prev_val = 0u64;
for (idx, &val) in test_cases.iter().enumerate() {
let mut encoded = Vec::new();
encode_u64_varint(val, &mut encoded);
let (decoded, len) = decode_u64_varint(&encoded).expect("decode failed");
assert_eq!(decoded, val);
assert_eq!(len, encoded.len());
if idx > 0 {
assert!(prev_val < val, "test values must be strictly increasing");
assert!(
prev_encoded < encoded,
"encoded bytes must preserve order: {:?} < {:?}",
prev_encoded,
encoded
);
}
prev_val = val;
prev_encoded = encoded;
}
}
#[test]
fn test_scope_and_binary_keys_non_penetration() {
let scopes = [
ScopeId::DEFAULT,
ScopeId::new(1001, 88),
ScopeId::new(1_000_000, 999_999_999),
];
let binary_keys: Vec<&[u8]> = vec![
b"",
b"\x00",
b"\x00\x00",
b"\x00\x00\x01\x02kmalicious_key",
b"\x00\x00\xff\xff\xfe\xfd",
b"user\x00profile\x00data",
b"\xff\xfe\xfd\xfc\xfb\xfa",
b"colon:separated:key:with:\x00:binary",
&[0x00, 0x00, 0x80, 0x01, 0x6b, 0x74, 0x65, 0x73, 0x74],
];
for &scope in &scopes {
for &raw_k in &binary_keys {
let encoded_key = scope.encode_key(raw_k);
assert_eq!(
ScopeId::extract_user_key(&encoded_key),
Some(raw_k),
"Failed for scope {scope:?}, key: {raw_k:?}"
);
if !scope.is_default() {
assert_eq!(ScopeId::parse_scope(&encoded_key), Some(scope));
}
let meta_k = scope.encode_meta_key(b'h', raw_k);
assert_eq!(
ScopeId::extract_user_key(&meta_k),
Some(raw_k),
"Failed for meta key: {raw_k:?}"
);
let binary_fields: Vec<&[u8]> = vec![
b"",
b"\x00",
b"\x00\x00\x00\x00",
b"normal_field",
b"field\x00with\x00nulls",
b"\xff\xff\xff\xff",
];
for &field in &binary_fields {
let version = 0x123456789ABCDEF0u64;
let sub_k = scope.encode_sub_key(b'h', raw_k, version, field);
let extracted = ScopeId::extract_user_key(&sub_k);
assert_eq!(
extracted,
Some(raw_k),
"SubKey extraction penetrated into subkey/version! key={raw_k:?}, field={field:?}"
);
}
}
}
let s1_0 = ScopeId::new(1, 0);
let s1_1 = ScopeId::new(1, 1);
let s2_0 = ScopeId::new(2, 0);
let s2_1 = ScopeId::new(2, 1);
let k_s1_0 = s1_0.encode_key(b"shared_key");
let k_s1_1 = s1_1.encode_key(b"shared_key");
let k_s2_0 = s2_0.encode_key(b"shared_key");
let k_s2_1 = s2_1.encode_key(b"shared_key");
assert_ne!(k_s1_0, k_s1_1);
assert_ne!(k_s1_0, k_s2_0);
assert_ne!(k_s1_1, k_s2_1);
assert_ne!(k_s2_0, k_s2_1);
assert!(!k_s1_0.starts_with(&s1_1.flush_db_prefix()));
assert!(!k_s1_0.starts_with(&s2_0.flush_db_prefix()));
assert!(!k_s1_1.starts_with(&s1_0.flush_db_prefix()));
assert!(!k_s2_0.starts_with(&s1_0.tenant_prefix()));
}
#[test]
fn test_fjall_scope_prefix_scan_isolation() {
let dir = tempfile::tempdir().expect("create tempdir failed");
let conf = Conf::new(dir.path().to_str().unwrap());
let db = WeDb::open_with_conf(&conf).expect("open db failed");
let s1_0 = ScopeId::new(1, 0);
let s1_1 = ScopeId::new(1, 1);
let s1_2 = ScopeId::new(1, 2);
let s2_0 = ScopeId::new(2, 0);
let s2_1 = ScopeId::new(2, 1);
let s100_0 = ScopeId::new(100, 0);
let s1_0_keys = [
s1_0.encode_key(b"key1"),
s1_0.encode_key(b"key2"),
s1_0.encode_key(b"binary\x00key\x00test"),
s1_0.encode_key(b"\x00\x00fake_escape"),
];
for (i, k) in s1_0_keys.iter().enumerate() {
db.data
.insert(k.as_ref(), format!("val_1_0_{i}").as_bytes())
.unwrap();
}
let meta_1_0 = s1_0.encode_meta_key(b'h', b"hash_1_0");
db.meta.insert(&meta_1_0, b"hash_meta_data").unwrap();
let sub1_1_0 = s1_0.encode_sub_key(b'h', b"hash_1_0", 1, b"f1");
let sub2_1_0 = s1_0.encode_sub_key(b'h', b"hash_1_0", 1, b"f2\x00null");
db.data.insert(&sub1_1_0, b"v1").unwrap();
db.data.insert(&sub2_1_0, b"v2").unwrap();
let s1_1_keys = [
s1_1.encode_key(b"key1"),
s1_1.encode_key(b"another_key"),
s1_1.encode_key(b"\xff\xfe_max_key"),
];
for (i, k) in s1_1_keys.iter().enumerate() {
db.data
.insert(k.as_ref(), format!("val_1_1_{i}").as_bytes())
.unwrap();
}
let s1_2_k = s1_2.encode_key(b"only_db2_key");
db.data.insert(s1_2_k.as_ref(), b"val_db2").unwrap();
let s2_0_keys = [s2_0.encode_key(b"key1"), s2_0.encode_key(b"t2_key")];
for (i, k) in s2_0_keys.iter().enumerate() {
db.data
.insert(k.as_ref(), format!("val_2_0_{i}").as_bytes())
.unwrap();
}
let s2_1_k = s2_1.encode_key(b"key1");
db.data.insert(s2_1_k.as_ref(), b"val_2_1").unwrap();
let s100_0_k = s100_0.encode_key(b"key1");
db.data.insert(s100_0_k.as_ref(), b"val_100_0").unwrap();
let flush_prefix_1_0 = s1_0.flush_db_prefix();
let scanned_data_1_0: Vec<Vec<u8>> = db
.data
.prefix(&flush_prefix_1_0)
.map(|item| item.key().unwrap().to_vec())
.collect();
assert_eq!(
scanned_data_1_0.len(),
6,
"Tenant 1 DB 0 data items mismatch"
);
for k in &scanned_data_1_0 {
assert_eq!(ScopeId::parse_scope(k), Some(s1_0));
}
let scanned_meta_1_0: Vec<Vec<u8>> = db
.meta
.prefix(&flush_prefix_1_0)
.map(|item| item.key().unwrap().to_vec())
.collect();
assert_eq!(
scanned_meta_1_0.len(),
1,
"Tenant 1 DB 0 meta items mismatch"
);
assert_eq!(ScopeId::parse_scope(&scanned_meta_1_0[0]), Some(s1_0));
let flush_prefix_1_1 = s1_1.flush_db_prefix();
let scanned_data_1_1: Vec<Vec<u8>> = db
.data
.prefix(&flush_prefix_1_1)
.map(|item| item.key().unwrap().to_vec())
.collect();
assert_eq!(
scanned_data_1_1.len(),
3,
"Tenant 1 DB 1 data items mismatch"
);
for k in &scanned_data_1_1 {
assert_eq!(ScopeId::parse_scope(k), Some(s1_1));
}
let tenant_prefix_1 = s1_0.tenant_prefix();
let scanned_tenant_1: Vec<Vec<u8>> = db
.data
.prefix(&tenant_prefix_1)
.map(|item| item.key().unwrap().to_vec())
.collect();
assert_eq!(
scanned_tenant_1.len(),
10,
"Tenant 1 total items across all DBs mismatch"
);
for k in &scanned_tenant_1 {
let sc = ScopeId::parse_scope(k).unwrap();
assert_eq!(sc.tenant_id, 1, "Leaked into another tenant!");
}
let tenant_prefix_2 = s2_0.tenant_prefix();
let scanned_tenant_2: Vec<Vec<u8>> = db
.data
.prefix(&tenant_prefix_2)
.map(|item| item.key().unwrap().to_vec())
.collect();
assert_eq!(scanned_tenant_2.len(), 3, "Tenant 2 items mismatch");
for k in &scanned_tenant_2 {
let sc = ScopeId::parse_scope(k).unwrap();
assert_eq!(sc.tenant_id, 2);
}
let mut batch = db.db.batch();
for k in &scanned_data_1_0 {
batch.remove(&db.data, k);
}
for k in &scanned_meta_1_0 {
batch.remove(&db.meta, k);
}
batch.commit().unwrap();
assert_eq!(db.data.prefix(&flush_prefix_1_0).count(), 0);
assert_eq!(db.meta.prefix(&flush_prefix_1_0).count(), 0);
assert_eq!(db.data.prefix(&flush_prefix_1_1).count(), 3);
assert_eq!(db.data.prefix(s1_2.flush_db_prefix()).count(), 1);
assert_eq!(db.data.prefix(s2_0.flush_db_prefix()).count(), 2);
assert_eq!(db.data.prefix(s100_0.flush_db_prefix()).count(), 1);
}
#[test]
fn test_oppv_lexicographical_range_ordering() {
let scopes = [
ScopeId::new(1, 0),
ScopeId::new(1, 1),
ScopeId::new(1, 127),
ScopeId::new(1, 128),
ScopeId::new(1, 16511),
ScopeId::new(1, 16512),
ScopeId::new(1, 2113663),
ScopeId::new(1, 2113664),
ScopeId::new(1, u64::MAX),
ScopeId::new(2, 0),
ScopeId::new(127, 0),
ScopeId::new(128, 0),
ScopeId::new(16511, 0),
ScopeId::new(16512, 0),
ScopeId::new(1_000_000, 0),
ScopeId::new(u64::MAX, u64::MAX),
];
for i in 0..scopes.len() - 1 {
let p1 = scopes[i].flush_db_prefix();
let p2 = scopes[i + 1].flush_db_prefix();
assert!(
p1 < p2,
"Lexicographical ordering violation: {:?} ({:?}) vs {:?} ({:?})",
scopes[i],
p1,
scopes[i + 1],
p2
);
}
}