mod common;
use std::sync::Mutex;
static WAL_ENV_LOCK: Mutex<()> = Mutex::new(());
fn journal_path_for(target: &std::path::Path) -> std::path::PathBuf {
let dir = target.parent().unwrap_or_else(|| std::path::Path::new("."));
let basename = target
.file_name()
.and_then(|s| s.to_str())
.unwrap_or("unknown");
dir.join(format!(
".atomwrite.journal.{}{}",
basename, ".atomwrite.journal.json"
))
}
#[test]
fn g119_l2_write_with_wal_leaves_no_sidecar_on_success() {
let _guard = WAL_ENV_LOCK.lock().expect("lock");
let dir = tempfile::tempdir().expect("tempdir");
let target = dir.path().join("alvo.txt");
std::fs::write(&target, "seed").expect("seed");
let output = common::atomwrite()
.args(["--workspace", dir.path().to_str().unwrap(), "write"])
.arg(&target)
.env("ATOMWRITE_WAL", "1")
.write_stdin("novo conteudo")
.output()
.expect("run");
assert!(output.status.success(), "exit: {:?}", output.status);
let sidecar = journal_path_for(&target);
assert!(
!sidecar.exists(),
"sidecar must be removed by L2 Drop guard after success, found: {}",
sidecar.display()
);
}
#[test]
fn g119_l2_write_failure_keeps_sidecar_for_recovery() {
let _guard = WAL_ENV_LOCK.lock().expect("lock");
let dir = tempfile::tempdir().expect("tempdir");
let target = dir.path().join("alvo.txt");
std::fs::write(&target, "seed").expect("seed");
let mut perms = std::fs::metadata(dir.path()).expect("meta").permissions();
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
perms.set_mode(0o555);
}
std::fs::set_permissions(dir.path(), perms).expect("chmod 0555");
let output = common::atomwrite()
.args(["--workspace", dir.path().to_str().unwrap(), "write"])
.arg(&target)
.env("ATOMWRITE_WAL", "1")
.write_stdin("novo conteudo")
.output()
.expect("run");
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mut p = std::fs::metadata(dir.path()).expect("meta").permissions();
p.set_mode(0o755);
std::fs::set_permissions(dir.path(), p).expect("restore");
}
assert!(
!output.status.success(),
"write must fail when dir is read-only, got: {:?}",
output.status
);
}
#[test]
fn g119_l5_wal_stats_reports_journal_state() {
let _guard = WAL_ENV_LOCK.lock().expect("lock");
let dir = tempfile::tempdir().expect("tempdir");
let target = dir.path().join("alvo.txt");
std::fs::write(&target, "seed").expect("seed");
let sidecar = journal_path_for(&target);
std::fs::write(
&sidecar,
"{\"phase\":\"started\",\"op_id\":\"abc123\",\"started_at_unix\":1700000000}\n\
{\"phase\":\"committed\",\"op_id\":\"abc123\",\"committed_at_unix\":1700000001}\n",
)
.expect("seed sidecar");
let output = common::atomwrite()
.args([
"--workspace",
dir.path().to_str().unwrap(),
"--no-auto-heal",
"wal-stats",
])
.output()
.expect("run");
assert!(output.status.success(), "exit: {:?}", output.status);
let events = common::parse_ndjson(&output.stdout);
assert_eq!(events.len(), 1);
let stats = &events[0];
assert!(stats["total_journals"].is_u64());
assert!(stats["by_state"].is_object());
assert!(stats["by_state"]["committed"].is_u64());
assert!(stats["by_directory"].is_array());
assert!(stats["auto_heal_recommended"].is_boolean());
let committed = stats["by_state"]["committed"].as_u64().unwrap();
assert!(
committed >= 1,
"expected at least 1 committed, got {committed}"
);
}
#[test]
fn g119_l5_wal_stats_reports_zeros_on_empty_workspace() {
let _guard = WAL_ENV_LOCK.lock().expect("lock");
let dir = tempfile::tempdir().expect("tempdir");
let output = common::atomwrite()
.args([
"--workspace",
dir.path().to_str().unwrap(),
"--no-auto-heal",
"wal-stats",
])
.output()
.expect("run");
assert!(output.status.success());
let events = common::parse_ndjson(&output.stdout);
assert_eq!(events[0]["total_journals"], 0);
assert_eq!(events[0]["by_state"]["committed"], 0);
assert_eq!(events[0]["by_state"]["started"], 0);
assert_eq!(events[0]["oldest_journal_age_secs"], 0);
assert_eq!(events[0]["total_size_bytes"], 0);
assert_eq!(events[0]["auto_heal_recommended"], false);
}
#[test]
fn g119_l3_startup_auto_heal_reaps_stale_committed() {
let _guard = WAL_ENV_LOCK.lock().expect("lock");
let dir = tempfile::tempdir().expect("tempdir");
let target = dir.path().join("alvo.txt");
std::fs::write(&target, "seed").expect("seed");
let sidecar = journal_path_for(&target);
std::fs::write(
&sidecar,
"{\"phase\":\"started\",\"op_id\":\"abc\",\"started_at_unix\":1}\n\
{\"phase\":\"committed\",\"op_id\":\"abc\",\"committed_at_unix\":1}\n",
)
.expect("seed");
let output = common::atomwrite()
.args(["--workspace", dir.path().to_str().unwrap(), "read"])
.arg(&target)
.output()
.expect("run");
assert!(
output.status.success(),
"read must succeed even when L3 runs first; stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
assert!(
!sidecar.exists(),
"L3 startup pass must reap the stale Committed sidecar"
);
}
#[test]
fn g119_l3_no_auto_heal_preserves_stale_committed() {
let _guard = WAL_ENV_LOCK.lock().expect("lock");
let dir = tempfile::tempdir().expect("tempdir");
let target = dir.path().join("alvo.txt");
std::fs::write(&target, "seed").expect("seed");
let sidecar = journal_path_for(&target);
std::fs::write(
&sidecar,
"{\"phase\":\"started\",\"op_id\":\"abc\",\"started_at_unix\":1}\n\
{\"phase\":\"committed\",\"op_id\":\"abc\",\"committed_at_unix\":1}\n",
)
.expect("seed");
let output = common::atomwrite()
.args([
"--workspace",
dir.path().to_str().unwrap(),
"--no-auto-heal",
"read",
])
.arg(&target)
.output()
.expect("run");
assert!(output.status.success());
assert!(
sidecar.exists(),
"--no-auto-heal must leave the stale sidecar untouched"
);
}
#[test]
fn g119_l4_sentinel_preserves_sidecar_on_successful_write() {
let _guard = WAL_ENV_LOCK.lock().expect("lock");
let dir = tempfile::tempdir().expect("tempdir");
std::fs::write(dir.path().join(".atomwrite_no_wal"), "").expect("sentinel");
let target = dir.path().join("with_sentinel.txt");
std::fs::write(&target, "seed").expect("seed");
let output = common::atomwrite()
.args([
"--workspace",
dir.path().to_str().unwrap(),
"--no-auto-heal",
"write",
"--wal-policy",
"always",
])
.arg(&target)
.write_stdin("novo conteudo")
.output()
.expect("run");
assert!(
output.status.success(),
"write must succeed; stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
let sidecar = journal_path_for(&target);
assert!(
sidecar.exists(),
"L4 h4_sentinel must preserve the sidecar even after a successful write"
);
}