#![cfg(all(unix, feature = "daemon-client"))]
use std::path::PathBuf;
use std::time::Duration;
fn leindexd_bin() -> PathBuf {
PathBuf::from(env!("CARGO_BIN_EXE_leindexd"))
}
struct ChildGuard {
child: std::process::Child,
}
impl Drop for ChildGuard {
fn drop(&mut self) {
let _ = self.child.kill();
let _ = self.child.wait();
}
}
#[tokio::test]
async fn test_spawn_helper_launches_daemon_and_waits_for_socket() {
let dir = tempfile::tempdir().expect("tempdir");
let run_dir = dir.path().to_path_buf();
let socket_path = run_dir.join("d.sock");
assert!(!socket_path.exists(), "socket must not exist before spawn");
let child = std::process::Command::new(leindexd_bin())
.arg("--socket")
.arg(&socket_path)
.arg("--idle-timeout-secs")
.arg("10")
.env("LEINDEX_HOME", dir.path())
.stdin(std::process::Stdio::null())
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.spawn()
.expect("spawn leindexd");
let guard = ChildGuard { child };
let start = tokio::time::Instant::now();
let appeared = leindex::cli::daemon::spawn::spawn_bounded_socket_wait(
&socket_path,
Duration::from_secs(15),
)
.await;
let elapsed = start.elapsed();
assert!(
appeared,
"socket must appear within bounded timeout (elapsed: {elapsed:?})"
);
assert!(
elapsed < Duration::from_secs(15),
"socket appeared but took too long: {elapsed:?}"
);
let sidecar = dir.path().join("run").join("daemon.endpoint");
let sidecar_deadline = std::time::Instant::now() + Duration::from_secs(5);
while !sidecar.exists() && std::time::Instant::now() < sidecar_deadline {
tokio::time::sleep(Duration::from_millis(10)).await;
}
assert!(
sidecar.exists(),
"endpoint sidecar must exist after daemon binds"
);
let sidecar_bytes = std::fs::read(&sidecar).expect("read sidecar");
let endpoint: serde_json::Value =
serde_json::from_slice(&sidecar_bytes).expect("parse sidecar JSON");
assert_eq!(
endpoint["socket_path"],
serde_json::json!(socket_path.to_string_lossy().to_string()),
"sidecar must record the correct socket path"
);
assert_eq!(
endpoint["protocol_version"],
serde_json::json!(leindex::cli::daemon::handshake::DAEMON_PROTOCOL_VERSION),
"sidecar must record the current protocol version"
);
drop(guard);
}
#[tokio::test]
async fn test_spawn_helper_bounded_wait_times_out() {
let dir = tempfile::tempdir().expect("tempdir");
let socket_path = dir.path().join("never-appears.sock");
let start = tokio::time::Instant::now();
let appeared = leindex::cli::daemon::spawn::spawn_bounded_socket_wait(
&socket_path,
Duration::from_millis(500),
)
.await;
let elapsed = start.elapsed();
assert!(!appeared, "bounded wait must return false on timeout");
assert!(
elapsed >= Duration::from_millis(400),
"must wait at least ~500ms: {elapsed:?}"
);
assert!(
elapsed < Duration::from_secs(2),
"must not wait unreasonably long: {elapsed:?}"
);
}