use super::tests::tmp_dir;
use crate::Store;
use crate::config::{AppendFsync, Config};
fn persist_cfg(dir: &std::path::Path) -> Config {
Config::default()
.with_persist(dir)
.with_ttl_reaper_manual()
.with_appendfsync(AppendFsync::Always)
}
fn reopen_cfg(dir: &std::path::Path) -> Config {
Config::default().with_persist(dir).with_ttl_reaper_manual()
}
#[test]
fn replay_covers_bitmap_verbs() {
let dir = tmp_dir("replay-bitmap");
{
let s = Store::open(persist_cfg(&dir)).unwrap();
s.setbit(b"bits", 7, 1).unwrap();
s.setrange(b"range", 5, b"hello").unwrap();
}
let s2 = Store::open(reopen_cfg(&dir)).unwrap();
assert_eq!(s2.getbit(b"bits", 7).unwrap(), 1, "SETBIT lost on replay");
assert_eq!(
s2.getrange(b"range", 5, 9).unwrap(),
b"hello".to_vec(),
"SETRANGE lost on replay"
);
drop(s2);
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn replay_covers_hash_verbs() {
let dir = tmp_dir("replay-hash");
{
let s = Store::open(persist_cfg(&dir)).unwrap();
s.hsetnx(b"h", b"f", b"v").unwrap();
s.hincrbyfloat(b"hf", b"score", 2.5).unwrap();
}
let s2 = Store::open(reopen_cfg(&dir)).unwrap();
assert_eq!(
s2.hget(b"h", b"f").unwrap(),
Some(b"v".to_vec()),
"HSETNX lost on replay"
);
assert_eq!(
s2.hget(b"hf", b"score").unwrap(),
Some(b"2.5".to_vec()),
"HINCRBYFLOAT lost on replay"
);
drop(s2);
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn replay_covers_list_verbs() {
let dir = tmp_dir("replay-list");
{
let s = Store::open(persist_cfg(&dir)).unwrap();
s.rpush(b"l", &[b"a", b"c"]).unwrap();
s.linsert(b"l", true, b"c", b"b").unwrap();
}
let s2 = Store::open(reopen_cfg(&dir)).unwrap();
assert_eq!(
s2.lrange(b"l", 0, -1).unwrap(),
vec![b"a".to_vec(), b"b".to_vec(), b"c".to_vec()],
"LINSERT lost on replay"
);
drop(s2);
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn replay_covers_rename_verbs() {
let dir = tmp_dir("replay-rename");
{
let s = Store::open(persist_cfg(&dir)).unwrap();
s.set(b"src", b"v1").unwrap();
s.rename(b"src", b"dst").unwrap();
s.set(b"src2", b"v2").unwrap();
s.renamenx(b"src2", b"dst2").unwrap();
}
let s2 = Store::open(reopen_cfg(&dir)).unwrap();
assert_eq!(s2.get(b"src").unwrap(), None, "RENAME src survived replay");
assert_eq!(
s2.get(b"dst").unwrap(),
Some(b"v1".to_vec()),
"RENAME dst lost on replay"
);
assert_eq!(
s2.get(b"dst2").unwrap(),
Some(b"v2".to_vec()),
"RENAMENX dst lost on replay"
);
drop(s2);
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn replay_covers_zset_removal_verbs() {
let dir = tmp_dir("replay-zset-rm");
{
let s = Store::open(persist_cfg(&dir)).unwrap();
let members: &[(f64, &[u8])] =
&[(1.0, b"a"), (2.0, b"b"), (3.0, b"c"), (4.0, b"d")];
s.zadd(b"zp", members).unwrap();
s.zpopmin(b"zp", 1).unwrap(); s.zadd(b"zr", members).unwrap();
s.zremrangebyrank(b"zr", 0, 0).unwrap(); s.zadd(b"zs", members).unwrap();
s.zremrangebyscore(b"zs", 3.5, 5.0).unwrap(); }
let s2 = Store::open(reopen_cfg(&dir)).unwrap();
assert_eq!(s2.zscore(b"zp", b"a").unwrap(), None, "ZPOPMIN lost on replay");
assert_eq!(s2.zcard(b"zp").unwrap(), 3);
assert_eq!(
s2.zscore(b"zr", b"a").unwrap(),
None,
"ZREMRANGEBYRANK lost on replay"
);
assert_eq!(s2.zcard(b"zr").unwrap(), 3);
assert_eq!(
s2.zscore(b"zs", b"d").unwrap(),
None,
"ZREMRANGEBYSCORE lost on replay"
);
assert_eq!(s2.zcard(b"zs").unwrap(), 3);
drop(s2);
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn copy_survives_reopen() {
let dir = tmp_dir("replay-copy");
{
let s = Store::open(persist_cfg(&dir)).unwrap();
s.set(b"src", b"payload").unwrap();
assert!(s.copy(b"src", b"dst", false).unwrap());
}
let s2 = Store::open(reopen_cfg(&dir)).unwrap();
assert_eq!(
s2.get(b"dst").unwrap(),
Some(b"payload".to_vec()),
"COPY dst never reached the AOF"
);
drop(s2);
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn spop_replay_removes_exactly_the_popped_members() {
let dir = tmp_dir("replay-spop");
let (popped, remaining_before): (Vec<Vec<u8>>, Vec<Vec<u8>>);
{
let s = Store::open(persist_cfg(&dir)).unwrap();
s.sadd(b"s", &[b"a", b"b", b"c", b"d", b"e"]).unwrap();
popped = s.spop(b"s", 2).unwrap();
let mut m = s.smembers(b"s").unwrap();
m.sort();
remaining_before = m;
}
let s2 = Store::open(reopen_cfg(&dir)).unwrap();
let mut remaining_after = s2.smembers(b"s").unwrap();
remaining_after.sort();
assert_eq!(
remaining_after, remaining_before,
"SPOP replay diverged: popped {popped:?} live but a different \
member set after reopen"
);
drop(s2);
let _ = std::fs::remove_dir_all(&dir);
}