use super::super::spawn::which_in_path_env;
use super::super::{ExecError, SpawnTool, ToolCall, ToolExecutor};
use super::fixtures::{AGENT_ID, FixedClock, HarnessRoot, StepDir, driver_target};
use serde_json::json;
use std::sync::atomic::AtomicBool;
use tempfile::TempDir;
#[test]
fn spawn_error_when_resolved_binary_is_not_executable() {
let root = HarnessRoot::new();
let bin = root.dir.path().join(super::super::TOOLS_DIR).join(format!(
"{}{}",
super::super::EXTERNAL_PREFIX,
"not-exec"
));
std::fs::write(&bin, b"not a real binary").unwrap();
let clock = FixedClock::default();
let step = StepDir::new();
let exec = SpawnTool::new(root.path(), &clock, driver_target());
let err = exec
.execute(
ToolCall {
id: "tu_e1",
name: "not-exec",
input: &json!({}),
},
&step.path,
&AtomicBool::new(false),
)
.unwrap_err();
match err {
ExecError::Spawn { name, .. } => assert_eq!(name, "not-exec"),
other => panic!("expected Spawn, got {other:?}"),
}
}
#[test]
fn io_error_when_step_dir_is_a_file() {
let root = HarnessRoot::new();
root.install("anything", "true");
let scratch = TempDir::new().unwrap();
let bogus_step = scratch.path().join("steps").join(AGENT_ID).join("001");
std::fs::create_dir_all(bogus_step.parent().unwrap()).unwrap();
std::fs::write(&bogus_step, b"i am a file").unwrap();
std::fs::create_dir_all(crate::workspace::agent_worktree(scratch.path(), AGENT_ID)).unwrap();
let clock = FixedClock::default();
let exec = SpawnTool::new(root.path(), &clock, driver_target());
let err = exec
.execute(
ToolCall {
id: "tu_e2",
name: "anything",
input: &json!({}),
},
&bogus_step,
&AtomicBool::new(false),
)
.unwrap_err();
match err {
ExecError::Io { dir, .. } => {
assert!(dir.ends_with("tu_e2"), "wrong dir in error: {:?}", dir);
}
other => panic!("expected Io, got {other:?}"),
}
}
fn declined_step_dir(step_dir: &std::path::Path) -> ExecError {
let root = HarnessRoot::new();
root.install("anything", "true");
let clock = FixedClock::default();
let exec = SpawnTool::new(root.path(), &clock, driver_target());
exec.execute(
ToolCall {
id: "tu_nw",
name: "anything",
input: &json!({}),
},
step_dir,
&AtomicBool::new(false),
)
.unwrap_err()
}
#[test]
fn no_worktree_when_the_step_dir_is_not_the_workspace_shape() {
let scratch = TempDir::new().unwrap();
let shapeless = scratch.path().join("elsewhere").join("001");
std::fs::create_dir_all(&shapeless).unwrap();
match declined_step_dir(&shapeless) {
ExecError::NoWorktree { name, step_dir } => {
assert_eq!(name, "anything");
assert_eq!(step_dir, shapeless);
}
other => panic!("expected NoWorktree, got {other:?}"),
}
}
#[test]
fn no_worktree_when_the_agents_worktree_is_not_materialized() {
let scratch = TempDir::new().unwrap();
let step_dir = scratch.path().join("steps").join(AGENT_ID).join("001");
std::fs::create_dir_all(&step_dir).unwrap();
match declined_step_dir(&step_dir) {
ExecError::NoWorktree { step_dir: d, .. } => assert_eq!(d, step_dir),
other => panic!("expected NoWorktree, got {other:?}"),
}
assert!(!step_dir.join(super::super::STEP_TOOLS_SUBDIR).exists());
}
#[test]
fn which_in_path_misses_when_no_dir_carries_the_binary() {
let empty = TempDir::new().unwrap();
assert_eq!(
which_in_path_env("lernie-tool-nope", Some(empty.path().as_os_str())),
None
);
}
#[test]
fn which_in_path_live_env_returns_a_value_for_a_real_binary() {
use super::super::spawn::which_in_path_env as wpe;
let path = std::env::var_os("PATH");
let hit = wpe("sh", path.as_deref());
assert!(hit.is_some(), "expected /bin/sh or similar on PATH");
}
#[test]
fn live_which_in_path_reads_path_env_without_panicking() {
let _ = super::super::spawn::which_in_path("lernie-tool-definitely-not-installed");
}