use crate::harness::types::{ExecInvocation, ExecResult};
use tokio::process::Command;
pub async fn execute(invocation: &ExecInvocation) -> Result<ExecResult, String> {
if invocation.command.is_empty() {
return Err("Empty command".to_string());
}
let mut cmd = Command::new(&invocation.command[0]);
cmd.args(&invocation.command[1..])
.current_dir(&invocation.cwd)
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped());
if let Some(ref env) = invocation.env {
for (key, value) in env {
cmd.env(key, value);
}
}
let child = cmd
.spawn()
.map_err(|e| format!("Failed to spawn process: {}", e))?;
let output = child
.wait_with_output()
.await
.map_err(|e| format!("Failed to wait for process: {}", e))?;
Ok(ExecResult {
exit_code: output.status.code().unwrap_or(-1),
stdout: String::from_utf8_lossy(&output.stdout).to_string(),
stderr: String::from_utf8_lossy(&output.stderr).to_string(),
})
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_execute_echo() {
let invocation = ExecInvocation {
command: vec!["echo".to_string(), "hello".to_string()],
cwd: ".".to_string(),
env: None,
};
let result = execute(&invocation).await.unwrap();
assert_eq!(result.exit_code, 0);
assert_eq!(result.stdout.trim(), "hello");
}
#[tokio::test]
async fn test_execute_nonexistent_command() {
let invocation = ExecInvocation {
command: vec!["nonexistent_command_xyz".to_string()],
cwd: ".".to_string(),
env: None,
};
let result = execute(&invocation).await;
assert!(result.is_err());
}
#[tokio::test]
async fn test_execute_exit_code() {
let invocation = ExecInvocation {
command: vec!["sh".to_string(), "-c".to_string(), "exit 42".to_string()],
cwd: ".".to_string(),
env: None,
};
let result = execute(&invocation).await.unwrap();
assert_eq!(result.exit_code, 42);
}
#[tokio::test]
async fn test_execute_empty_command() {
let invocation = ExecInvocation {
command: vec![],
cwd: ".".to_string(),
env: None,
};
let result = execute(&invocation).await;
assert!(result.is_err());
assert!(result.unwrap_err().contains("Empty command"));
}
#[tokio::test]
async fn test_execute_captures_stderr() {
let invocation = ExecInvocation {
command: vec![
"sh".to_string(),
"-c".to_string(),
"echo err >&2".to_string(),
],
cwd: ".".to_string(),
env: None,
};
let result = execute(&invocation).await.unwrap();
assert_eq!(result.exit_code, 0);
assert_eq!(result.stderr.trim(), "err");
}
}