use supercode::tools::{
ApplyPatchTool, PersistentShellTool, SandboxPolicy, Tool, ToolContext, WriteFileTool,
};
fn ctx() -> (ToolContext, std::path::PathBuf) {
use std::sync::atomic::{AtomicU64, Ordering};
static N: AtomicU64 = AtomicU64::new(0);
let dir = std::env::temp_dir().join(format!(
"sc-tools-{}-{}",
std::process::id(),
N.fetch_add(1, Ordering::SeqCst)
));
std::fs::create_dir_all(&dir).unwrap();
(ToolContext::new(dir.clone()), dir)
}
#[tokio::test]
async fn apply_patch_add_update_delete_move() {
let (ctx, dir) = ctx();
let tool = ApplyPatchTool;
std::fs::write(dir.join("keep.rs"), "fn main() {\n old();\n}\n").unwrap();
std::fs::write(dir.join("gone.txt"), "bye").unwrap();
let patch = "\
*** Begin Patch
*** Add File: src/new.rs
+pub fn added() -> u8 {
+ 42
+}
*** Update File: keep.rs
@@
fn main() {
- old();
+ fresh();
}
*** Delete File: gone.txt
*** End Patch
";
let out = tool
.execute(serde_json::json!({ "patch": patch }), &ctx)
.await
.unwrap();
assert!(out.contains("A src/new.rs"), "{out}");
assert!(out.contains("U keep.rs"), "{out}");
assert!(out.contains("D gone.txt"), "{out}");
let added = std::fs::read_to_string(dir.join("src/new.rs")).unwrap();
assert!(added.contains("pub fn added") && added.contains("42"));
let updated = std::fs::read_to_string(dir.join("keep.rs")).unwrap();
assert!(updated.contains("fresh();") && !updated.contains("old();"));
assert!(updated.contains("fn main()"));
assert!(!dir.join("gone.txt").exists());
let mv = "\
*** Begin Patch
*** Update File: keep.rs
*** Move to: renamed.rs
@@
- fresh();
+ fresher();
*** End Patch
";
let out = tool
.execute(serde_json::json!({ "patch": mv }), &ctx)
.await
.unwrap();
assert!(out.contains("M keep.rs -> renamed.rs"), "{out}");
assert!(!dir.join("keep.rs").exists());
assert!(std::fs::read_to_string(dir.join("renamed.rs"))
.unwrap()
.contains("fresher();"));
std::fs::remove_dir_all(&dir).ok();
}
#[tokio::test]
async fn apply_patch_reports_unmatched_hunk() {
let (ctx, dir) = ctx();
std::fs::write(dir.join("f.txt"), "hello world").unwrap();
let tool = ApplyPatchTool;
let patch = "\
*** Begin Patch
*** Update File: f.txt
@@
-not present
+replacement
*** End Patch
";
let err = tool
.execute(serde_json::json!({ "patch": patch }), &ctx)
.await
.unwrap_err()
.to_string();
assert!(err.contains("did not match"), "{err}");
std::fs::remove_dir_all(&dir).ok();
}
#[tokio::test]
async fn apply_patch_is_registered_as_a_builtin() {
use supercode::ToolRegistry;
let reg = ToolRegistry::with_builtins();
assert!(
reg.get("apply_patch").is_some(),
"apply_patch must be a built-in tool"
);
assert!(
reg.get("shell").is_some(),
"persistent shell must be a built-in tool"
);
}
#[tokio::test]
async fn persistent_shell_keeps_state_across_calls() {
let (ctx, dir) = ctx();
let sh = PersistentShellTool::default();
let r = sh
.execute(serde_json::json!({"command": "MYVAR=hello123"}), &ctx)
.await
.unwrap();
assert!(r.contains("exit code: 0"), "{r}");
let r = sh
.execute(serde_json::json!({"command": "echo $MYVAR"}), &ctx)
.await
.unwrap();
assert!(r.contains("hello123"), "env var did not persist: {r}");
std::fs::create_dir_all(dir.join("sub")).unwrap();
sh.execute(serde_json::json!({"command": "cd sub"}), &ctx)
.await
.unwrap();
let r = sh
.execute(serde_json::json!({"command": "pwd"}), &ctx)
.await
.unwrap();
assert!(r.contains("/sub"), "cwd did not persist: {r}");
let r = sh
.execute(serde_json::json!({"command": "false"}), &ctx)
.await
.unwrap();
assert!(r.contains("exit code: 1"), "{r}");
std::fs::remove_dir_all(&dir).ok();
}
#[tokio::test]
async fn sandbox_read_only_and_workspace_write() {
let (mut ctx, dir) = ctx();
let w = WriteFileTool;
ctx.sandbox = SandboxPolicy::ReadOnly;
let err = w
.execute(serde_json::json!({"path": "a.txt", "content": "x"}), &ctx)
.await
.unwrap_err()
.to_string();
assert!(err.contains("read-only"), "{err}");
assert!(!dir.join("a.txt").exists());
ctx.sandbox = SandboxPolicy::WorkspaceWrite;
w.execute(
serde_json::json!({"path": "inside.txt", "content": "ok"}),
&ctx,
)
.await
.unwrap();
assert_eq!(
std::fs::read_to_string(dir.join("inside.txt")).unwrap(),
"ok"
);
let outside = std::env::temp_dir().join(format!("sc-escape-{}.txt", std::process::id()));
let err = w
.execute(
serde_json::json!({"path": outside.to_string_lossy(), "content": "no"}),
&ctx,
)
.await
.unwrap_err()
.to_string();
assert!(err.contains("outside the workspace"), "{err}");
assert!(!outside.exists());
let err = w
.execute(
serde_json::json!({"path": "../escape.txt", "content": "no"}),
&ctx,
)
.await
.unwrap_err()
.to_string();
assert!(err.contains("outside the workspace"), "{err}");
ctx.sandbox = SandboxPolicy::DangerFullAccess;
w.execute(
serde_json::json!({"path": outside.to_string_lossy(), "content": "yes"}),
&ctx,
)
.await
.unwrap();
assert!(outside.exists());
std::fs::remove_file(&outside).ok();
std::fs::remove_dir_all(&dir).ok();
}
#[tokio::test]
async fn sandbox_blocks_apply_patch_writes() {
let (mut ctx, dir) = ctx();
ctx.sandbox = SandboxPolicy::ReadOnly;
let tool = ApplyPatchTool;
let patch = "*** Begin Patch\n*** Add File: blocked.rs\n+x\n*** End Patch\n";
let err = tool
.execute(serde_json::json!({"patch": patch}), &ctx)
.await
.unwrap_err()
.to_string();
assert!(err.contains("read-only"), "{err}");
assert!(!dir.join("blocked.rs").exists());
std::fs::remove_dir_all(&dir).ok();
}
#[tokio::test]
async fn persistent_shell_write_stdin() {
let (ctx, dir) = ctx();
let sh = PersistentShellTool::default();
let out = sh
.execute(
serde_json::json!({"write_stdin": "echo from_stdin_pipe\n"}),
&ctx,
)
.await
.unwrap();
assert!(out.contains("from_stdin_pipe"), "write_stdin output: {out}");
std::fs::remove_dir_all(&dir).ok();
}
#[tokio::test]
async fn update_plan_tool() {
use supercode::tools::UpdatePlanTool;
let (ctx, dir) = ctx();
let tool = UpdatePlanTool::default();
let out = tool
.execute(
serde_json::json!({"plan": [
{"step": "read code", "status": "completed"},
{"step": "fix bug", "status": "in_progress"},
{"step": "add test"}
]}),
&ctx,
)
.await
.unwrap();
assert!(out.contains("[x] read code"));
assert!(out.contains("[~] fix bug"));
assert!(out.contains("[ ] add test"));
assert_eq!(tool.current().len(), 3);
std::fs::remove_dir_all(&dir).ok();
}
#[cfg(target_os = "macos")]
#[tokio::test]
async fn bash_os_sandbox_blocks_writes() {
use supercode::tools::BashTool;
let (mut ctx, dir) = ctx();
let tool = BashTool::default();
ctx.sandbox = SandboxPolicy::ReadOnly;
let out = tool
.execute(
serde_json::json!({"command": "echo hi > blocked.txt; echo done"}),
&ctx,
)
.await
.unwrap();
assert!(
!dir.join("blocked.txt").exists(),
"read-only sandbox must block the write: {out}"
);
ctx.sandbox = SandboxPolicy::WorkspaceWrite;
tool.execute(serde_json::json!({"command": "echo hi > inside.txt"}), &ctx)
.await
.unwrap();
assert!(
dir.join("inside.txt").exists(),
"workspace-write must allow writes inside cwd"
);
let outside = std::env::temp_dir().join(format!("sc-bash-escape-{}.txt", std::process::id()));
let _ = std::fs::remove_file(&outside);
let cmd = format!("echo hi > {}", outside.display());
tool.execute(serde_json::json!({"command": cmd}), &ctx)
.await
.unwrap();
assert!(
!outside.exists(),
"workspace-write must block writes outside cwd"
);
ctx.sandbox = SandboxPolicy::DangerFullAccess;
tool.execute(serde_json::json!({"command": "echo hi > free.txt"}), &ctx)
.await
.unwrap();
assert!(dir.join("free.txt").exists());
std::fs::remove_dir_all(&dir).ok();
}
#[cfg(target_os = "macos")]
#[tokio::test]
async fn persistent_shell_os_sandbox_blocks_writes() {
let (mut ctx, dir) = ctx();
ctx.sandbox = SandboxPolicy::ReadOnly;
let sh = PersistentShellTool::default();
let out = sh
.execute(
serde_json::json!({"command": "echo hi > shell_blocked.txt; echo done"}),
&ctx,
)
.await
.unwrap();
assert!(
!dir.join("shell_blocked.txt").exists(),
"read-only sandbox must block the persistent shell's write: {out}"
);
std::fs::remove_dir_all(&dir).ok();
}
#[tokio::test]
async fn apply_patch_at_at_anchor_disambiguates_and_locates_insertions() {
let (ctx, dir) = ctx();
let tool = ApplyPatchTool;
std::fs::write(
dir.join("a.py"),
"def foo():\n return 1\n\ndef bar():\n return 1\n",
)
.unwrap();
let patch = "\
*** Begin Patch
*** Update File: a.py
@@ def bar():
- return 1
+ return 2
*** End Patch
";
tool.execute(serde_json::json!({ "patch": patch }), &ctx)
.await
.unwrap();
let after = std::fs::read_to_string(dir.join("a.py")).unwrap();
assert_eq!(
after, "def foo():\n return 1\n\ndef bar():\n return 2\n",
"anchor must target bar, leaving foo untouched: {after:?}"
);
let ins = "\
*** Begin Patch
*** Update File: a.py
@@ def foo():
+ # hello
*** End Patch
";
tool.execute(serde_json::json!({ "patch": ins }), &ctx)
.await
.unwrap();
let after = std::fs::read_to_string(dir.join("a.py")).unwrap();
assert!(
after.starts_with("def foo():\n # hello\n return 1\n"),
"insertion must follow the anchor line, not append at EOF: {after:?}"
);
let bad = "\
*** Begin Patch
*** Update File: a.py
@@ def nope():
- return 1
+ return 9
*** End Patch
";
assert!(tool
.execute(serde_json::json!({ "patch": bad }), &ctx)
.await
.is_err());
std::fs::remove_dir_all(&dir).ok();
}