Remove fixture races exposed by concurrent file IO

Read only atomically published journal batches, not in-progress spool tempfiles.
Wait for a guest-ready notification before closing stdin in the EOF test,
rather than treating process admission as handler readiness.

Card: SAU-359
This commit is contained in:
SAU-359 implementer
2026-10-30 21:17:28 +00:00
parent 0c1ef74f6e
commit 95b27f0948
2 changed files with 14 additions and 8 deletions
+11 -8
View File
@@ -208,7 +208,11 @@ async fn journal_recovers_after_sqlite_write_failure() {
call(&h, "core.log", json!({})).await; call(&h, "core.log", json!({})).await;
let batches: Vec<Value> = std::fs::read_dir(w.0.join("logs/core/failed-batches")) let batches: Vec<Value> = std::fs::read_dir(w.0.join("logs/core/failed-batches"))
.unwrap() .unwrap()
.map(|file| serde_json::from_slice(&std::fs::read(file.unwrap().path()).unwrap()).unwrap()) .map(Result::unwrap)
// The writer atomically publishes .json files; its temporary files can
// still be incomplete while later process-exit entries are spooling.
.filter(|file| file.path().extension().is_some_and(|ext| ext == "json"))
.map(|file| serde_json::from_slice(&std::fs::read(file.path()).unwrap()).unwrap())
.collect(); .collect();
assert!( assert!(
batches batches
@@ -1104,17 +1108,16 @@ async fn stop_closes_stdin_and_waits_for_exit() {
let w = TestWorld::new(); let w = TestWorld::new();
let h = w.start().await; let h = w.start().await;
let hc = h.clone(); let hc = h.clone();
let mut events = h.subscribe();
let pending = tokio::spawn(async move { hc.call("probe.wait_eof", json!({})).await }); let pending = tokio::spawn(async move { hc.call("probe.wait_eof", json!({})).await });
// Admission is not readiness: stop may otherwise close stdin before the
// request reaches the guest, so wait for the handler to enter its EOF loop.
let pid = tokio::time::timeout(std::time::Duration::from_secs(20), async { let pid = tokio::time::timeout(std::time::Duration::from_secs(20), async {
loop { loop {
if let Some((pid, _)) = h let event = events.recv().await.unwrap();
.processes() if event["params"]["name"] == "waiting-eof" {
.into_iter() break event["params"]["params"]["pid"].clone();
.find(|(_, p)| p.method == "wait_eof")
{
break pid;
} }
tokio::task::yield_now().await;
} }
}) })
.await .await
+3
View File
@@ -83,6 +83,9 @@ fn unknown_id() {
} }
fn wait_eof(lines: &mut Lines) { fn wait_eof(lines: &mut Lines) {
std::fs::write("/state/waiting", "yes").unwrap(); std::fs::write("/state/waiting", "yes").unwrap();
send(json!({"jsonrpc":"2.0","method":"core.notify","params":{
"name":"waiting-eof", "params":{"pid":std::env::var("SAUSAGE_PID").unwrap()}
}}));
for _ in lines {} for _ in lines {}
std::fs::write("/state/stopped", "yes").unwrap(); std::fs::write("/state/stopped", "yes").unwrap();
} }