use super::*;
#[test]
fn test_concurrent_snapshots_different_files() {
let temp = TempDir::new().unwrap();
heddle(&["init"], Some(temp.path())).unwrap();
fs::write(temp.path().join("base.txt"), "base").unwrap();
heddle(&["capture", "-m", "Base"], Some(temp.path())).unwrap();
let barrier = Arc::new(Barrier::new(3));
let temp_path = temp.path().to_path_buf();
let handles: Vec<_> = (0..3)
.map(|i| {
let barrier = barrier.clone();
let path = temp_path.clone();
thread::spawn(move || {
barrier.wait();
fs::write(
path.join(format!("thread{}.txt", i)),
format!("content {}", i),
)
.unwrap();
heddle(&["capture", "-m", &format!("Thread {}", i)], Some(&path))
})
})
.collect();
let results: Vec<_> = handles.into_iter().map(|h| h.join().unwrap()).collect();
let successes = results.iter().filter(|r| r.is_ok()).count();
assert!(
successes >= 1,
"at least one snapshot should succeed, got {}",
successes
);
}
#[test]
fn test_concurrent_status_and_snapshot() {
let temp = TempDir::new().unwrap();
heddle(&["init"], Some(temp.path())).unwrap();
for i in 0..100 {
fs::write(temp.path().join(format!("file{}.txt", i)), format!("{}", i)).unwrap();
}
let barrier = Arc::new(Barrier::new(2));
let temp_path = temp.path().to_path_buf();
let handle1 = {
let barrier = barrier.clone();
let path = temp_path.clone();
thread::spawn(move || {
barrier.wait();
heddle(&["status"], Some(&path))
})
};
let handle2 = {
let barrier = barrier.clone();
let path = temp_path.clone();
thread::spawn(move || {
barrier.wait();
fs::write(path.join("new.txt"), "new").unwrap();
heddle(&["capture", "-m", "New"], Some(&path))
})
};
let result1 = handle1.join().unwrap();
let result2 = handle2.join().unwrap();
assert!(
result1.is_ok() || result2.is_ok(),
"at least one operation should succeed"
);
}
#[test]
fn test_concurrent_reads_same_repo() {
let temp = TempDir::new().unwrap();
heddle(&["init"], Some(temp.path())).unwrap();
for i in 0..50 {
fs::write(temp.path().join(format!("file{}.txt", i)), format!("{}", i)).unwrap();
}
heddle(&["capture", "-m", "Initial"], Some(temp.path())).unwrap();
let barrier = Arc::new(Barrier::new(5));
let temp_path = temp.path().to_path_buf();
let handles: Vec<_> = (0..5)
.map(|_| {
let barrier = barrier.clone();
let path = temp_path.clone();
thread::spawn(move || {
barrier.wait();
heddle(&["log"], Some(&path))
})
})
.collect();
for handle in handles {
let result = handle.join().unwrap();
assert!(
result.is_ok(),
"concurrent reads should all succeed: {:?}",
result.err()
);
}
}
#[test]
fn test_workspace_isolation() {
let temp = TempDir::new().unwrap();
heddle(&["init"], Some(temp.path())).unwrap();
fs::write(temp.path().join("shared.txt"), "shared").unwrap();
heddle(&["capture", "-m", "Shared"], Some(temp.path())).unwrap();
let workspace1 = temp.path().join("workspace1");
let workspace2 = temp.path().join("workspace2");
fs::create_dir(&workspace1).unwrap();
fs::create_dir(&workspace2).unwrap();
fs::write(workspace1.join("agent1.txt"), "agent 1 work").unwrap();
fs::write(workspace2.join("agent2.txt"), "agent 2 work").unwrap();
assert_exists(workspace1.join("agent1.txt"), "workspace 1 file");
assert_exists(workspace2.join("agent2.txt"), "workspace 2 file");
assert_not_exists(
workspace1.join("agent2.txt"),
"workspace 1 shouldn't have agent2 file",
);
assert_not_exists(
workspace2.join("agent1.txt"),
"workspace 2 shouldn't have agent1 file",
);
}