From f47150cec8fd00d376d08790cd6b8af82d96d131 Mon Sep 17 00:00:00 2001 From: Marco Allegretti Date: Wed, 11 Mar 2026 09:24:34 +0100 Subject: [PATCH] test(appd): add runtime supervisor integration test supervisor_transitions_through_ready_to_stopped (unix only): - Writes a temp shell script that prints 'READY' and exits. - Sets WEFT_RUNTIME_BIN to the script path; restores env after test. - Calls runtime::supervise() and verifies final session state is Stopped. - Verifies AppReady was broadcast via the registry broadcast channel. - Runs with tokio flavor='current_thread' to avoid concurrent env mutation. Wraps set_var/remove_var in unsafe blocks (required since Rust 1.93). --- crates/weft-appd/src/main.rs | 43 ++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/crates/weft-appd/src/main.rs b/crates/weft-appd/src/main.rs index 0d9d486..93aa2c5 100644 --- a/crates/weft-appd/src/main.rs +++ b/crates/weft-appd/src/main.rs @@ -373,4 +373,47 @@ mod tests { let reg = SessionRegistry::default(); assert!(matches!(reg.state(42), AppStateKind::NotFound)); } + + #[cfg(unix)] + #[tokio::test(flavor = "current_thread")] + async fn supervisor_transitions_through_ready_to_stopped() { + use std::os::unix::fs::PermissionsExt; + + let script = + std::env::temp_dir().join(format!("weft_test_runtime_{}.sh", std::process::id())); + std::fs::write(&script, "#!/bin/sh\necho READY\n").unwrap(); + std::fs::set_permissions(&script, std::fs::Permissions::from_mode(0o755)).unwrap(); + + let prior = std::env::var("WEFT_RUNTIME_BIN").ok(); + // SAFETY: single-threaded test (flavor = "current_thread"); no concurrent env access. + unsafe { std::env::set_var("WEFT_RUNTIME_BIN", &script) }; + + let registry: Registry = Arc::new(Mutex::new(SessionRegistry::default())); + let mut rx = registry.lock().await.subscribe(); + let session_id = registry.lock().await.launch("test.app"); + + runtime::supervise(session_id, "test.app", Arc::clone(®istry)) + .await + .unwrap(); + + assert!(matches!( + registry.lock().await.state(session_id), + AppStateKind::Stopped + )); + + let notification = rx.try_recv(); + assert!(matches!( + notification, + Ok(Response::AppReady { session_id: sid }) if sid == session_id + )); + + let _ = std::fs::remove_file(&script); + // SAFETY: single-threaded test; restoring env to prior state. + unsafe { + match prior { + Some(v) => std::env::set_var("WEFT_RUNTIME_BIN", v), + None => std::env::remove_var("WEFT_RUNTIME_BIN"), + } + } + } }