use aok::Void;
use tempfile::tempdir;
use wedb_embed::WeDb;
use wedb_embed::key_composer::KeyComposer;
use wedb_embed::list::{ListMeta, PosSpec};
#[ctor::ctor(unsafe)]
fn _log_init() {
log_init::init();
}
#[test]
fn test_list_metadata_and_indexing() -> Void {
let meta = ListMeta::new(1700000000000, 100);
assert_eq!(meta.head, ListMeta::INITIAL_INDEX);
assert_eq!(meta.tail, ListMeta::INITIAL_INDEX);
assert_eq!(meta.size(), 0);
let enc = meta.encode();
assert_eq!(enc.len(), ListMeta::ENCODED_SIZE);
let dec = ListMeta::decode(&enc).expect("decode failed");
assert_eq!(dec.head, ListMeta::INITIAL_INDEX);
assert_eq!(dec.tail, ListMeta::INITIAL_INDEX);
let kv_enc = meta.encode_kvrocks();
assert_eq!(kv_enc.len(), ListMeta::KVROCKS_ENCODED_SIZE);
let kv_dec = ListMeta::decode(&kv_enc).expect("decode kvrocks failed");
assert_eq!(kv_dec.head, ListMeta::INITIAL_INDEX);
assert_eq!(kv_dec.tail, ListMeta::INITIAL_INDEX);
Ok(())
}
#[test]
fn test_list_push_and_pop() -> Void {
let dir = tempdir()?;
let db = WeDb::open(dir.path())?;
assert_eq!(db.rpush("lkey", &["a", "b", "c"])?, 3);
assert_eq!(db.lpush("lkey", &["z"])?, 4);
assert_eq!(db.llen("lkey")?, 4);
assert_eq!(db.lpushx("lkey", &["first"])?, 5);
assert_eq!(db.rpushx("lkey", &["last"])?, 6);
assert_eq!(db.lpushx("nonexistent", &["val"])?, 0);
assert_eq!(db.rpushx("nonexistent", &["val"])?, 0);
let range = db.lrange("lkey", 0, -1)?;
assert_eq!(
range,
vec![
b"first".to_vec(),
b"z".to_vec(),
b"a".to_vec(),
b"b".to_vec(),
b"c".to_vec(),
b"last".to_vec(),
]
);
assert_eq!(db.lpop("lkey", 1)?, vec![b"first".to_vec()]);
assert_eq!(db.rpop("lkey", 1)?, vec![b"last".to_vec()]);
assert_eq!(db.llen("lkey")?, 4);
assert_eq!(
db.lpop("lkey", 5)?,
vec![b"z".to_vec(), b"a".to_vec(), b"b".to_vec(), b"c".to_vec()]
);
assert_eq!(db.llen("lkey")?, 0);
assert_eq!(db.lpop("lkey", 1)?, Vec::<Vec<u8>>::new());
Ok(())
}
#[test]
fn test_list_index_and_set() -> Void {
let dir = tempdir()?;
let db = WeDb::open(dir.path())?;
db.rpush("list_idx", &["v0", "v1", "v2", "v3"])?;
assert_eq!(db.lindex("list_idx", 0)?, Some(b"v0".to_vec()));
assert_eq!(db.lindex("list_idx", 2)?, Some(b"v2".to_vec()));
assert_eq!(db.lindex("list_idx", -1)?, Some(b"v3".to_vec()));
assert_eq!(db.lindex("list_idx", -4)?, Some(b"v0".to_vec()));
assert_eq!(db.lindex("list_idx", -5)?, None);
assert_eq!(db.lindex("list_idx", 100)?, None);
db.lset("list_idx", 1, "v1_updated")?;
assert_eq!(db.lindex("list_idx", 1)?, Some(b"v1_updated".to_vec()));
db.lset("list_idx", -1, "v3_updated")?;
assert_eq!(db.lindex("list_idx", 3)?, Some(b"v3_updated".to_vec()));
assert!(db.lset("list_idx", 100, "out_of_range").is_err());
assert!(db.lset("list_idx", -10, "out_of_range").is_err());
assert!(db.lset("nonexistent_list", 0, "val").is_err());
Ok(())
}
#[test]
fn test_list_lrange_edge_cases() -> Void {
let dir = tempdir()?;
let db = WeDb::open(dir.path())?;
db.rpush("lrkey", &["a", "b", "c", "d"])?;
assert_eq!(
db.lrange("lrkey", 0, 3)?,
vec![b"a".to_vec(), b"b".to_vec(), b"c".to_vec(), b"d".to_vec()]
);
assert_eq!(
db.lrange("lrkey", 0, 100)?,
vec![b"a".to_vec(), b"b".to_vec(), b"c".to_vec(), b"d".to_vec()]
);
assert_eq!(
db.lrange("lrkey", -100, 100)?,
vec![b"a".to_vec(), b"b".to_vec(), b"c".to_vec(), b"d".to_vec()]
);
assert_eq!(
db.lrange("lrkey", -100, 1)?,
vec![b"a".to_vec(), b"b".to_vec()]
);
assert_eq!(
db.lrange("lrkey", -2, -1)?,
vec![b"c".to_vec(), b"d".to_vec()]
);
assert_eq!(db.lrange("lrkey", 0, -10)?, Vec::<Vec<u8>>::new());
assert_eq!(db.lrange("lrkey", -10, -5)?, Vec::<Vec<u8>>::new());
assert_eq!(db.lrange("lrkey", 5, 10)?, Vec::<Vec<u8>>::new());
assert_eq!(db.lrange("lrkey", 3, 1)?, Vec::<Vec<u8>>::new());
assert_eq!(db.lrange("lrkey", -1, -2)?, Vec::<Vec<u8>>::new());
assert_eq!(
db.lrange("nonexistent_lrkey", 0, -1)?,
Vec::<Vec<u8>>::new()
);
Ok(())
}
#[test]
fn test_list_insert_and_rem_and_pos() -> Void {
let dir = tempdir()?;
let db = WeDb::open(dir.path())?;
db.rpush("lops", &["hello", "foo", "bar", "foo", "world"])?;
assert_eq!(db.linsert("lops", true, "bar", "before_bar")?, 6);
assert_eq!(db.linsert("lops", false, "bar", "after_bar")?, 7);
assert_eq!(db.linsert("lops", true, "nonexistent", "val")?, -1);
assert_eq!(db.linsert("nonexistent_key", true, "bar", "val")?, 0);
let pos_first = db.lpos("lops", "foo", PosSpec::default())?;
assert_eq!(pos_first, vec![1]);
let pos_rev = db.lpos("lops", "foo", PosSpec::new().with_rank(-1))?;
assert_eq!(pos_rev, vec![5]);
let pos_all = db.lpos("lops", "foo", PosSpec::new().with_rank(1).with_count(10))?;
assert_eq!(pos_all, vec![1, 5]);
let pos_limit = db.lpos(
"lops",
"foo",
PosSpec::new().with_rank(1).with_count(10).with_max_len(3),
)?;
assert_eq!(pos_limit, vec![1]);
assert!(db.lpos("lops", "foo", PosSpec::new().with_rank(0)).is_err());
assert_eq!(db.lrem("lops", 1, "foo")?, 1);
assert_eq!(db.lrem("lops", -1, "foo")?, 1);
assert_eq!(db.lrem("lops", 0, "bar")?, 1);
assert_eq!(db.llen("lops")?, 4);
db.rpush("del_all", &["x", "x", "x"])?;
assert_eq!(db.lrem("del_all", 0, "x")?, 3);
assert_eq!(db.llen("del_all")?, 0);
Ok(())
}
#[test]
fn test_list_lmove_and_ltrim() -> Void {
let dir = tempdir()?;
let db = WeDb::open(dir.path())?;
db.rpush("src_list", &["1", "2", "3"])?;
let rot = db.lmove("src_list", "src_list", false, true)?;
assert_eq!(rot, Some(b"3".to_vec()));
let range = db.lrange("src_list", 0, -1)?;
assert_eq!(range, vec![b"3".to_vec(), b"1".to_vec(), b"2".to_vec()]);
let rpl = db.rpoplpush("src_list", "src_list")?;
assert_eq!(rpl, Some(b"2".to_vec()));
assert_eq!(
db.lrange("src_list", 0, -1)?,
vec![b"2".to_vec(), b"3".to_vec(), b"1".to_vec()]
);
let moved = db.lmove("src_list", "dst_list", true, false)?;
assert_eq!(moved, Some(b"2".to_vec()));
assert_eq!(db.llen("src_list")?, 2);
assert_eq!(db.llen("dst_list")?, 1);
db.ltrim("src_list", 0, 0)?;
assert_eq!(db.llen("src_list")?, 1);
assert_eq!(db.lindex("src_list", 0)?, Some(b"3".to_vec()));
db.ltrim("src_list", 5, 2)?;
assert_eq!(db.llen("src_list")?, 0);
Ok(())
}
#[test]
fn test_list_expiration_behavior() -> Void {
let dir = tempdir()?;
let db = WeDb::open(dir.path())?;
db.rpush("exp_list", &["e1", "e2", "e3"])?;
assert_eq!(db.llen("exp_list")?, 3);
let kc = KeyComposer::new("default");
let meta_k = kc.list_meta("exp_list");
let mut exp_meta = ListMeta::new(1000, 1); exp_meta.base.size = 3;
db.meta_ks.insert(meta_k.as_bytes(), exp_meta.encode())?;
assert_eq!(db.llen("exp_list")?, 0);
assert_eq!(db.lrange("exp_list", 0, -1)?, Vec::<Vec<u8>>::new());
assert_eq!(db.lindex("exp_list", 0)?, None);
assert_eq!(db.lpop("exp_list", 1)?, Vec::<Vec<u8>>::new());
assert_eq!(db.rpop("exp_list", 1)?, Vec::<Vec<u8>>::new());
assert_eq!(
db.lpos("exp_list", "e1", PosSpec::default())?,
Vec::<i64>::new()
);
assert_eq!(db.linsert("exp_list", true, "e1", "x")?, 0);
assert_eq!(db.lrem("exp_list", 0, "e1")?, 0);
assert_eq!(db.lpushx("exp_list", &["x"])?, 0);
assert_eq!(db.rpushx("exp_list", &["x"])?, 0);
assert!(db.lset("exp_list", 0, "x").is_err());
assert_eq!(db.lmove("exp_list", "other", true, false)?, None);
assert_eq!(db.lpush("exp_list", &["fresh"])?, 1);
assert_eq!(db.lrange("exp_list", 0, -1)?, vec![b"fresh".to_vec()]);
Ok(())
}
#[test]
fn test_list_linsert_extremities() -> Void {
let dir = tempdir()?;
let db = WeDb::open(dir.path())?;
db.rpush("ins_list", &["head", "mid", "tail"])?;
assert_eq!(db.linsert("ins_list", true, "head", "new_head")?, 4);
assert_eq!(db.lindex("ins_list", 0)?, Some(b"new_head".to_vec()));
assert_eq!(db.linsert("ins_list", false, "tail", "new_tail")?, 5);
assert_eq!(db.lindex("ins_list", -1)?, Some(b"new_tail".to_vec()));
assert_eq!(
db.lrange("ins_list", 0, -1)?,
vec![
b"new_head".to_vec(),
b"head".to_vec(),
b"mid".to_vec(),
b"tail".to_vec(),
b"new_tail".to_vec(),
]
);
Ok(())
}
#[test]
fn test_list_lmove_expired_dst() -> Void {
let dir = tempdir()?;
let db = WeDb::open(dir.path())?;
db.rpush("src", &["item1", "item2"])?;
db.rpush("dst_exp", &["stale1", "stale2"])?;
let kc = KeyComposer::new("default");
let meta_k = kc.list_meta("dst_exp");
let mut exp_meta = ListMeta::new(1000, 1);
exp_meta.base.size = 2;
db.meta_ks.insert(meta_k.as_bytes(), exp_meta.encode())?;
let res = db.lmove("src", "dst_exp", true, false)?;
assert_eq!(res, Some(b"item1".to_vec()));
assert_eq!(db.llen("src")?, 1);
assert_eq!(db.llen("dst_exp")?, 1);
assert_eq!(db.lrange("dst_exp", 0, -1)?, vec![b"item1".to_vec()]);
Ok(())
}
#[test]
fn test_list_ttl_and_persist() -> Void {
let dir = tempdir()?;
let db = WeDb::open(dir.path())?;
db.rpush("ttl_list", &["v1", "v2", "v3"])?;
assert_eq!(db.lttl("ttl_list")?, -1);
assert_eq!(db.lttl("nonexistent_list")?, -2);
assert!(db.lexpire("ttl_list", 300)?);
let ttl = db.lttl("ttl_list")?;
assert!(ttl > 0 && ttl <= 300_000);
assert!(db.lpersist("ttl_list")?);
assert_eq!(db.lttl("ttl_list")?, -1);
assert!(!db.lpersist("ttl_list")?);
let future_ms = (ts_::sec() + 600) * 1000;
assert!(db.lexpireat("ttl_list", future_ms)?);
let ttl2 = db.lttl("ttl_list")?;
assert!(ttl2 > 0);
assert!(!db.lexpire("nonexistent_list", 100)?);
assert!(!db.lexpireat("nonexistent_list", future_ms)?);
assert!(!db.lpersist("nonexistent_list")?);
Ok(())
}
#[test]
fn test_list_kvrocks_comprehensive_cases() -> Void {
let dir = tempdir()?;
let db = WeDb::open(dir.path())?;
db.rpush(
"lpos_keys",
&["a", "b", "c", "d", "b", "e", "b", "f", "b", "g"],
)?; assert_eq!(db.llen("lpos_keys")?, 10);
assert_eq!(db.lpos("lpos_keys", "b", PosSpec::default())?, vec![1]);
assert_eq!(
db.lpos("lpos_keys", "b", PosSpec::new().with_rank(2))?,
vec![4]
);
assert_eq!(
db.lpos("lpos_keys", "b", PosSpec::new().with_rank(-1))?,
vec![8]
);
assert_eq!(
db.lpos("lpos_keys", "b", PosSpec::new().with_rank(-2))?,
vec![6]
);
assert_eq!(
db.lpos("lpos_keys", "b", PosSpec::new().with_count(0))?,
vec![1, 4, 6, 8]
);
assert_eq!(
db.lpos("lpos_keys", "b", PosSpec::new().with_count(2))?,
vec![1, 4]
);
assert_eq!(
db.lpos("lpos_keys", "b", PosSpec::new().with_rank(-1).with_count(2))?,
vec![8, 6]
);
assert_eq!(
db.lpos("lpos_keys", "b", PosSpec::new().with_max_len(3))?,
vec![1]
);
assert_eq!(
db.lpos("lpos_keys", "b", PosSpec::new().with_max_len(1))?,
Vec::<i64>::new()
);
db.rpush("lrem_test", &["x", "1", "2", "x", "3", "4", "x"])?;
assert_eq!(db.lrem("lrem_test", 1, "x")?, 1);
assert_eq!(
db.lrange("lrem_test", 0, -1)?,
vec![
b"1".to_vec(),
b"2".to_vec(),
b"x".to_vec(),
b"3".to_vec(),
b"4".to_vec(),
b"x".to_vec()
]
);
assert_eq!(db.lrem("lrem_test", -1, "x")?, 1);
assert_eq!(
db.lrange("lrem_test", 0, -1)?,
vec![
b"1".to_vec(),
b"2".to_vec(),
b"x".to_vec(),
b"3".to_vec(),
b"4".to_vec()
]
);
assert_eq!(db.lrem("lrem_test", 0, "x")?, 1);
assert_eq!(
db.lrange("lrem_test", 0, -1)?,
vec![b"1".to_vec(), b"2".to_vec(), b"3".to_vec(), b"4".to_vec()]
);
db.ltrim("lrem_test", -3, -2)?; assert_eq!(
db.lrange("lrem_test", 0, -1)?,
vec![b"2".to_vec(), b"3".to_vec()]
);
assert_eq!(db.llen("lrem_test")?, 2);
Ok(())
}