use apcore::{Config, Executor, Registry};
use apcore_cli::security::sandbox::{ModuleExecutionError, Sandbox};
use serde_json::json;
fn make_test_executor() -> Executor {
Executor::new(Registry::new(), Config::default())
}
#[tokio::test]
async fn test_sandbox_disabled_passes_through_to_executor() {
let sandbox = Sandbox::new(false, 0);
assert!(!sandbox.is_enabled(), "sandbox must report disabled");
let executor = make_test_executor();
let result = sandbox
.execute("math.add", json!({"a": 1, "b": 2}), &executor)
.await;
match &result {
Err(ModuleExecutionError::SpawnFailed(msg)) if msg.contains("not wired") => {
panic!("disabled sandbox must passthrough to executor, not return 'not wired'");
}
Err(ModuleExecutionError::ModuleError(_)) => {
}
Err(other) => panic!(
"disabled sandbox must surface ModuleError variant for executor \
failures so exit-code mapping stays consistent with direct exec; \
got: {other:?}"
),
Ok(_) => {
}
}
}
#[tokio::test]
#[ignore = "requires the compiled apcore-cli binary to be present at current_exe(); \
run manually after `cargo build` with `cargo test -- --ignored`"]
async fn test_sandbox_enabled_spawns_subprocess() {
let sandbox = Sandbox::new(true, 5);
assert!(sandbox.is_enabled(), "sandbox must report enabled");
let executor = make_test_executor();
let result = sandbox
.execute("math.add", json!({"a": 1, "b": 2}), &executor)
.await;
match &result {
Err(ModuleExecutionError::SpawnFailed(msg)) if msg.contains("not found") => {
panic!(
"sandbox enabled path must not delegate to executor, \
must spawn subprocess instead. Got: {msg}"
);
}
_ => {} }
}
#[tokio::test]
async fn test_sandbox_enabled_timeout_returns_error() {
let sandbox = Sandbox::new(true, 1); let executor = make_test_executor();
let result = sandbox.execute("slow.module", json!({}), &executor).await;
assert!(
result.is_err(),
"sandbox with 1s timeout must return an error"
);
}
#[tokio::test]
async fn test_nonzero_exit_carries_stderr() {
let err = ModuleExecutionError::NonZeroExit {
module_id: "some.module".to_string(),
exit_code: 2,
stderr: "simulated panic: invalid state".to_string(),
};
let msg = err.to_string();
assert!(
msg.contains("simulated panic: invalid state"),
"NonZeroExit Display must include captured stderr, got: {msg}"
);
assert!(msg.contains("exited with code 2"));
}
#[tokio::test]
#[ignore = "requires the compiled apcore-cli binary to be present at current_exe(); \
run manually after `cargo build --bin apcore-cli` with `cargo test -- --ignored`"]
async fn test_sandbox_nonzero_exit_returns_error() {
let sandbox = Sandbox::new(true, 5); let executor = make_test_executor();
let result = sandbox
.execute("__nonexistent_module__", json!({}), &executor)
.await;
assert!(result.is_err(), "unknown module must result in an error");
}