use lex_vcs::{OpId, OpLog, Operation, OperationKind, OperationRecord, StageTransition};
use std::collections::BTreeSet;
fn add_op(i: usize) -> OperationRecord {
let sig = format!("fn-{i:04}::Int->Int");
let stage = format!("stg-{i:04}");
let op = Operation::new(
OperationKind::AddFunction {
sig_id: sig.clone(),
stage_id: stage.clone(),
effects: BTreeSet::new(),
budget_cost: None,
},
[],
);
OperationRecord::new(
op,
StageTransition::Create { sig_id: sig, stage_id: stage },
)
}
#[test]
fn one_thousand_loose_ops_round_trip_through_repack() {
let tmp = tempfile::tempdir().unwrap();
let log = OpLog::open(tmp.path()).unwrap();
let mut originals: Vec<(OpId, OperationRecord)> = Vec::with_capacity(1000);
for i in 0..1000 {
let rec = add_op(i);
log.put(&rec).unwrap();
originals.push((rec.op_id.clone(), rec));
}
let packed = log.repack(0).unwrap();
assert_eq!(packed, 1000);
let ops_dir = tmp.path().join("ops");
let n_loose = std::fs::read_dir(&ops_dir).unwrap()
.filter_map(|e| e.ok())
.filter(|e| {
let n = e.file_name().to_string_lossy().to_string();
n.ends_with(".json") && !n.starts_with("pack-")
})
.count();
assert_eq!(n_loose, 0);
let pack_count = std::fs::read_dir(&ops_dir).unwrap()
.filter_map(|e| e.ok())
.filter(|e| e.path().extension().is_some_and(|x| x == "pack"))
.count();
let idx_count = std::fs::read_dir(&ops_dir).unwrap()
.filter_map(|e| e.ok())
.filter(|e| e.path().extension().is_some_and(|x| x == "idx"))
.count();
assert_eq!(pack_count, 1);
assert_eq!(idx_count, 1);
for (op_id, original) in &originals {
let loaded = log.get(op_id).unwrap()
.unwrap_or_else(|| panic!("op_id {op_id} not found in pack"));
assert_eq!(&loaded, original,
"post-repack record for {op_id} differs from pre-repack");
}
let all = log.list_all().unwrap();
assert_eq!(all.len(), 1000);
let all_ids: BTreeSet<OpId> = all.into_iter().map(|r| r.op_id).collect();
let original_ids: BTreeSet<OpId> = originals.iter()
.map(|(id, _)| id.clone()).collect();
assert_eq!(all_ids, original_ids);
}
#[test]
fn rerunning_repack_on_already_packed_store_is_idempotent() {
let tmp = tempfile::tempdir().unwrap();
let log = OpLog::open(tmp.path()).unwrap();
for i in 0..5 {
log.put(&add_op(i)).unwrap();
}
log.repack(0).unwrap();
let pack_name_1 = pack_filename(tmp.path());
let n2 = log.repack(1).unwrap();
assert_eq!(n2, 0, "no loose files left → no repack");
assert_eq!(pack_name_1, pack_filename(tmp.path()),
"pack filename must be unchanged across no-op repack");
}
fn pack_filename(root: &std::path::Path) -> String {
std::fs::read_dir(root.join("ops")).unwrap()
.filter_map(|e| e.ok())
.find(|e| e.path().extension().is_some_and(|x| x == "pack"))
.unwrap()
.file_name().into_string().unwrap()
}