use std::path::Path;
use std::sync::atomic::Ordering;
use atomwrite::atomic::{AtomicWriteOptions, atomic_write};
use atomwrite::wal::{WalPolicy, journal_path};
fn tempfiles_under(dir: &Path) -> Vec<String> {
std::fs::read_dir(dir)
.expect("read_dir")
.filter_map(|e| e.ok())
.map(|e| e.file_name().to_string_lossy().into_owned())
.filter(|n| n.starts_with(atomwrite::constants::TEMPFILE_PREFIX))
.collect()
}
#[test]
fn cancel_discards_the_journal_and_failure_keeps_it() {
let dir = tempfile::tempdir().expect("tempdir");
let ws = dir.path();
let options = |syntax_check: bool| AtomicWriteOptions {
wal_policy: WalPolicy::Always,
syntax_check,
..AtomicWriteOptions::default()
};
let opts = options(false);
let broken = ws.join("broken.json");
let failed = atomic_write(&broken, b"{\"a\": [1, 2", &options(true), ws);
assert!(failed.is_err(), "the syntax check must reject the content");
assert!(
journal_path(&broken).exists(),
"a failed write must keep its journal for recovery"
);
let signal = atomwrite::signal::install_handlers().expect("handlers");
signal.flag().store(true, Ordering::Release);
let existing = ws.join("existing.txt");
std::fs::write(&existing, "original\n").expect("seed");
let err = atomic_write(&existing, b"replacement\n", &opts, ws)
.err()
.expect("a cancelled write is an error");
assert!(
matches!(
err.downcast_ref::<atomwrite::error::AtomwriteError>(),
Some(atomwrite::error::AtomwriteError::Cancelled { .. })
),
"expected Cancelled, got {err:#}"
);
assert_eq!(std::fs::read_to_string(&existing).unwrap(), "original\n");
assert!(
!journal_path(&existing).exists(),
"a cancelled write must not leave a journal"
);
let fresh = ws.join("fresh.txt");
assert!(atomic_write(&fresh, b"new\n", &opts, ws).is_err());
assert!(
!fresh.exists(),
"a cancelled write must not create the target"
);
assert!(!journal_path(&fresh).exists());
assert!(tempfiles_under(ws).is_empty(), "{:?}", tempfiles_under(ws));
}