Files
BrowserSkill/crates/bsk-cli/tests/auto_spawn.rs
T
2026-06-22 17:36:26 +08:00

77 lines
2.5 KiB
Rust

//! Verify `ensure_daemon` spawns the daemon when none is running.
//!
//! We can't call `ensure_daemon` directly from a test process because
//! `current_exe()` would point to the test binary, not `bsk`. Instead we
//! drive the same effect end-to-end via `bsk status` (which itself calls
//! into `ensure_daemon` in M3.3) — but for M3.2 we test the helper by
//! pointing `current_exe` indirection at the actual `bsk` binary through
//! a small shim test.
#![cfg(unix)]
use std::path::PathBuf;
use std::process::Command;
use std::time::{Duration, Instant};
use tempfile::TempDir;
fn bsk_bin() -> PathBuf {
PathBuf::from(env!("CARGO_BIN_EXE_bsk"))
}
fn wait_for_pid_exit(pid: i32, timeout: Duration) -> bool {
let deadline = Instant::now() + timeout;
while Instant::now() < deadline {
let alive = unsafe { libc::kill(pid, 0) } == 0;
if !alive {
return true;
}
std::thread::sleep(Duration::from_millis(50));
}
false
}
#[test]
fn ensure_daemon_idempotent_when_already_running() {
// Use BSK_HOME to isolate.
let tmp = TempDir::new().unwrap();
let home = tmp.path().join("bsk");
std::fs::create_dir_all(&home).unwrap();
// Start a daemon manually first.
let out = Command::new(bsk_bin())
.args(["daemon", "start", "--port", "0", "--daemon-idle", "60s"])
.env("BSK_HOME", &home)
.env("RUST_LOG", "warn")
.output()
.unwrap();
assert!(out.status.success());
let info_path = home.join("daemon.json");
let info: serde_json::Value =
serde_json::from_slice(&std::fs::read(&info_path).unwrap()).unwrap();
let pid_before = info["pid"].as_u64().unwrap() as i32;
// Now another `bsk daemon start` is invoked. Because the lock is
// held, the spawned child should exit quickly and the existing
// daemon should keep its pid.
let _ = Command::new(bsk_bin())
.args(["daemon", "start", "--port", "0", "--daemon-idle", "60s"])
.env("BSK_HOME", &home)
.env("RUST_LOG", "warn")
.output()
.unwrap();
let info_after: serde_json::Value =
serde_json::from_slice(&std::fs::read(&info_path).unwrap()).unwrap();
let pid_after = info_after["pid"].as_u64().unwrap() as i32;
assert_eq!(pid_before, pid_after, "daemon pid should not change");
// Clean up.
let _ = Command::new(bsk_bin())
.args(["daemon", "stop"])
.env("BSK_HOME", &home)
.output();
assert!(wait_for_pid_exit(pid_before, Duration::from_secs(5)));
}