#[cfg(test)]
mod tests {
use crate::cook::execution::command::*;
use crate::cook::execution::process::*;
use std::process::Stdio;
use std::time::Duration;
use tokio::process::Command;
#[cfg(unix)]
#[tokio::test]
async fn test_process_group_kill() {
let mut command = Command::new("sh");
command.arg("-c");
command.arg("(sleep 60 & sleep 60 & wait)");
command.stdout(Stdio::null());
command.stderr(Stdio::null());
command.process_group(0);
let child = command.spawn().expect("Failed to spawn process");
let mut process = UnifiedProcess::new(child, CommandType::Shell);
tokio::time::sleep(Duration::from_millis(100)).await;
let kill_result = process.kill().await;
assert!(kill_result.is_ok(), "Kill should succeed");
}
#[cfg(unix)]
#[tokio::test]
async fn test_kill_with_sigterm_then_sigkill() {
use nix::sys::signal::{self, Signal};
use nix::unistd::Pid;
let mut command = Command::new("sh");
command.arg("-c");
command.arg("trap '' TERM; sleep 60");
command.stdout(Stdio::null());
command.stderr(Stdio::null());
command.process_group(0);
let child = command.spawn().expect("Failed to spawn process");
let child_pid = child.id().expect("No PID") as i32;
let mut process = UnifiedProcess::new(child, CommandType::Shell);
let kill_result = process.kill().await;
assert!(
kill_result.is_ok(),
"Kill should succeed even if process ignores SIGTERM"
);
tokio::time::sleep(Duration::from_millis(200)).await;
let result = signal::kill(Pid::from_raw(child_pid), Signal::SIGCONT);
assert!(result.is_err(), "Process should be dead");
}
#[tokio::test]
async fn test_kill_already_dead_process() {
let mut command = Command::new("echo");
command.arg("test");
command.stdout(Stdio::null());
let child = command.spawn().expect("Failed to spawn process");
let mut process = UnifiedProcess::new(child, CommandType::Shell);
let _ = process.wait().await;
let kill_result = process.kill().await;
assert!(kill_result.is_ok() || kill_result.is_err());
}
#[tokio::test]
async fn test_resource_usage_after_kill() {
let mut command = Command::new("sleep");
command.arg("60");
command.stdout(Stdio::null());
let child = command.spawn().expect("Failed to spawn process");
let mut process = UnifiedProcess::new(child, CommandType::Shell);
tokio::time::sleep(Duration::from_millis(50)).await;
let _ = process.kill().await;
let usage = process.resource_usage();
let _ = usage.duration;
}
#[tokio::test]
async fn test_process_id_tracking() {
let mut command = Command::new("echo");
command.arg("test");
let child = command.spawn().expect("Failed to spawn process");
let child_id = child.id().expect("No PID");
let process = UnifiedProcess::new(child, CommandType::Shell);
assert_eq!(process.id().0, child_id);
}
#[cfg(unix)]
#[tokio::test]
async fn test_process_manager_spawn_with_process_group() {
let manager = ProcessManager::new();
let executable = ExecutableCommand::new("sh")
.arg("-c")
.arg("echo parent; echo child")
.with_type(CommandType::Shell);
let context = crate::cook::execution::executor::ExecutionContextInternal {
id: uuid::Uuid::new_v4(),
request: CommandRequest {
spec: CommandSpec::Shell {
command: "test".to_string(),
shell: None,
working_dir: None,
env: None,
},
execution_config: ExecutionConfig::default(),
context: ExecutionContext::default(),
metadata: CommandMetadata::new("test"),
},
resource_limits: None,
};
let result = manager.spawn(executable, &context).await;
if let Err(e) = &result {
if e.to_string().contains("Command not found") {
eprintln!("Skipping test: sh command not found");
return;
}
}
assert!(result.is_ok());
let mut process = result.unwrap();
let exit_status = process.wait().await;
assert!(exit_status.is_ok());
}
}