use async_trait::async_trait;
pub struct SandboxOutput {
pub stdout: String,
pub stderr: String,
pub exit_code: i32,
}
#[async_trait]
pub trait BashSandbox: Send + Sync {
async fn exec_command(
&self,
command: &str,
guest_workspace: &str,
) -> anyhow::Result<SandboxOutput>;
async fn shutdown(&self);
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Arc;
struct MockSandbox {
output: String,
exit_code: i32,
}
#[async_trait]
impl BashSandbox for MockSandbox {
async fn exec_command(
&self,
_command: &str,
_guest_workspace: &str,
) -> anyhow::Result<SandboxOutput> {
Ok(SandboxOutput {
stdout: self.output.clone(),
stderr: String::new(),
exit_code: self.exit_code,
})
}
async fn shutdown(&self) {}
}
#[tokio::test]
async fn test_mock_sandbox_success() {
let sandbox = MockSandbox {
output: "hello sandbox\n".into(),
exit_code: 0,
};
let result = sandbox
.exec_command("echo hello sandbox", "/workspace")
.await
.unwrap();
assert_eq!(result.stdout, "hello sandbox\n");
assert_eq!(result.exit_code, 0);
assert!(result.stderr.is_empty());
}
#[tokio::test]
async fn test_mock_sandbox_nonzero_exit() {
let sandbox = MockSandbox {
output: String::new(),
exit_code: 127,
};
let result = sandbox
.exec_command("nonexistent_cmd", "/workspace")
.await
.unwrap();
assert_eq!(result.exit_code, 127);
}
#[tokio::test]
async fn test_bash_sandbox_is_arc_send_sync() {
let sandbox: Arc<dyn BashSandbox> = Arc::new(MockSandbox {
output: "ok".into(),
exit_code: 0,
});
let result = sandbox.exec_command("true", "/workspace").await.unwrap();
assert_eq!(result.exit_code, 0);
}
}