use std::path::PathBuf;
use std::sync::Arc;
use std::thread;
use grex_core::manifest::read_all;
use grex_core::sync::__test_append_sync_event;
use tempfile::tempdir;
const PER_THREAD: usize = 50;
#[test]
fn two_threads_sync_append_are_serialised() {
let dir = tempdir().unwrap();
let log = Arc::new(dir.path().join(".grex").join("grex.jsonl"));
let lock = Arc::new(dir.path().join(".grex").join(".grex.lock"));
let handles: Vec<_> = (0..2)
.map(|tid| {
let log = Arc::clone(&log);
let lock = Arc::clone(&lock);
thread::spawn(move || -> Vec<String> {
let mut failures = Vec::new();
for i in 0..PER_THREAD {
let pack = format!("t{tid}");
let action = format!("act-{i}");
if let Err(e) = __test_append_sync_event(&log, &lock, &pack, &action) {
failures.push(e);
}
}
failures
})
})
.collect();
for h in handles {
let failures = h.join().unwrap();
assert!(failures.is_empty(), "unexpected append failures: {failures:?}");
}
let events = read_all(&log).expect("event log parses cleanly");
assert_eq!(events.len(), 2 * PER_THREAD, "total event count");
let raw = std::fs::read_to_string(&*log).unwrap();
let line_count = raw.lines().count();
assert_eq!(line_count, 2 * PER_THREAD, "raw line count matches event count");
for (i, line) in raw.lines().enumerate() {
let _: serde_json::Value = serde_json::from_str(line)
.unwrap_or_else(|e| panic!("line {i} is not valid JSON: {e}: `{line}`"));
}
}
#[test]
fn sync_append_creates_parent_dir_lazily() {
let dir = tempdir().unwrap();
let log: PathBuf = dir.path().join(".grex").join("grex.jsonl");
let lock: PathBuf = dir.path().join(".grex").join(".grex.lock");
assert!(!log.parent().unwrap().exists(), "precondition: .grex/ absent");
__test_append_sync_event(&log, &lock, "pack", "act").expect("first append");
assert!(log.exists(), "log created");
assert!(lock.exists(), "lock sidecar created");
}