use anyhow::{Context, Result};
use ralph::lock;
use std::fs;
use tempfile::TempDir;
#[cfg(unix)]
mod lock_support;
#[cfg(unix)]
#[test]
fn acquire_dir_lock_auto_cleans_stale_lock_when_forced() -> Result<()> {
let dir = TempDir::new().context("create temp dir")?;
let lock_dir = dir.path().join("lock");
fs::create_dir_all(&lock_dir)?;
let owner_path = lock_dir.join("owner");
let stale_pid = lock_support::spawn_exited_pid();
fs::write(
&owner_path,
format!(
"pid: {stale_pid}\nstarted_at: 2026-01-18T00:00:00Z\ncommand: old-ralph\nlabel: stale-lock\n"
),
)?;
let err = lock::acquire_dir_lock(&lock_dir, "new-proc", false).unwrap_err();
assert!(format!("{err:#}").to_lowercase().contains("stale pid"));
let _lock =
lock::acquire_dir_lock(&lock_dir, "new-proc", true).context("acquire with force")?;
assert!(lock_dir.exists());
assert!(owner_path.exists());
let owner_content = fs::read_to_string(&owner_path)?;
assert!(owner_content.contains("new-proc"));
assert!(owner_content.contains(&std::process::id().to_string()));
Ok(())
}
#[test]
fn acquire_dir_lock_does_not_clean_active_lock_even_if_forced() -> Result<()> {
let dir = TempDir::new().context("create temp dir")?;
let lock_dir = dir.path().join("lock");
let _lock1 = lock::acquire_dir_lock(&lock_dir, "proc1", false).context("acquire first lock")?;
let err = lock::acquire_dir_lock(&lock_dir, "proc2", true).unwrap_err();
let msg = format!("{err:#}");
assert!(msg.to_lowercase().contains("lock already held"));
assert!(msg.to_lowercase().contains("pid:"));
assert!(!msg.to_lowercase().contains("stale pid"));
Ok(())
}
#[cfg(unix)]
#[test]
fn acquire_dir_lock_with_force_clears_stale_lock_for_resume() -> Result<()> {
let dir = TempDir::new().context("create temp dir")?;
let repo_root = dir.path().to_path_buf();
fs::create_dir_all(repo_root.join(".ralph")).context("create .ralph dir")?;
let lock_dir = lock::queue_lock_dir(&repo_root);
fs::create_dir_all(&lock_dir)?;
let stale_pid = lock_support::spawn_exited_pid();
fs::write(
lock_dir.join("owner"),
format!(
"pid: {stale_pid}\nstarted_at: 2026-02-06T00:56:29Z\ncommand: ralph run loop --max-tasks 0\nlabel: run one\n"
),
)?;
assert!(lock_dir.exists(), "lock dir should exist");
let err = lock::acquire_dir_lock(&lock_dir, "test", false).unwrap_err();
assert!(
format!("{err:#}").contains("STALE PID"),
"expected stale PID error"
);
let lock = lock::acquire_dir_lock(&lock_dir, "run loop resume", true)?;
drop(lock);
assert!(
!lock_dir.exists(),
"expected lock dir to be removed on drop"
);
Ok(())
}