From 41f832458ee514ac99f16d02c02fb6f8bb9195cc Mon Sep 17 00:00:00 2001 From: atlas Date: Wed, 28 Oct 2026 11:29:35 +0100 Subject: [PATCH] write the review-classifier fake agent scripts from a child process --- swarm-review-classifier/src/acp.rs | 10 +++------ swarm-review-classifier/src/main.rs | 2 ++ swarm-review-classifier/src/run.rs | 5 +---- swarm-review-classifier/src/test_script.rs | 25 ++++++++++++++++++++++ 4 files changed, 31 insertions(+), 11 deletions(-) create mode 100644 swarm-review-classifier/src/test_script.rs diff --git a/swarm-review-classifier/src/acp.rs b/swarm-review-classifier/src/acp.rs index 40a15da3..7be0fe12 100644 --- a/swarm-review-classifier/src/acp.rs +++ b/swarm-review-classifier/src/acp.rs @@ -274,8 +274,6 @@ impl Sink for FinalText { #[cfg(test)] mod tests { - use std::os::unix::fs::PermissionsExt as _; - use swarm_wire::review_classifier::Prices; use super::*; @@ -347,12 +345,10 @@ done fn acp(dir: &Path) -> (Acp>, Arc) { let script = dir.join("agent.sh"); let log = dir.join("log"); - std::fs::write( + crate::test_script::write_executable( &script, - AGENT.replacen("log=\"$1\"", &format!("log='{}'", log.display()), 1), - ) - .expect("written"); - std::fs::set_permissions(&script, std::fs::Permissions::from_mode(0o755)).expect("chmod"); + &AGENT.replacen("log=\"$1\"", &format!("log='{}'", log.display()), 1), + ); let costs = Arc::new(Costs::default()); let acp = Acp { program: PathBuf::from("/nix/store/x-opencode/bin/opencode"), diff --git a/swarm-review-classifier/src/main.rs b/swarm-review-classifier/src/main.rs index a77e0fe2..a2cbd3a3 100644 --- a/swarm-review-classifier/src/main.rs +++ b/swarm-review-classifier/src/main.rs @@ -25,6 +25,8 @@ mod child_env; mod cost; mod prompt; mod run; +#[cfg(test)] +mod test_script; /// The cert-auth role this service logs in to the store as. `swarm-bao.nix` /// writes it, matching the leaf's subject. diff --git a/swarm-review-classifier/src/run.rs b/swarm-review-classifier/src/run.rs index 994c7eab..afe358e9 100644 --- a/swarm-review-classifier/src/run.rs +++ b/swarm-review-classifier/src/run.rs @@ -856,12 +856,9 @@ printf '{"type":"result","subtype":"success","is_error":false,"result":"ok"}\n' #[tokio::test] async fn claude_reads_the_shared_context_and_the_question_as_separate_blocks() { - use std::os::unix::fs::PermissionsExt as _; - let tmp = tempfile::tempdir().expect("a dir"); let script = tmp.path().join("claude.sh"); - std::fs::write(&script, CLAUDE).expect("written"); - std::fs::set_permissions(&script, std::fs::Permissions::from_mode(0o755)).expect("chmod"); + crate::test_script::write_executable(&script, CLAUDE); let claude = Claude { program: PathBuf::from("claude"), trampoline: script.clone(), diff --git a/swarm-review-classifier/src/test_script.rs b/swarm-review-classifier/src/test_script.rs new file mode 100644 index 00000000..a4bec28b --- /dev/null +++ b/swarm-review-classifier/src/test_script.rs @@ -0,0 +1,25 @@ +//! Fake agent scripts for the tests. + +use std::path::Path; + +/// Write the script `body` to `path` as an executable, from a child process +/// rather than from this one: a write fd on it held here could be inherited +/// by a child another test thread forks, and exec'ing a file some process +/// still holds open for writing fails with `ETXTBSY`. +/// +/// # Panics +/// +/// If `/bin/sh` cannot run or the write fails. +pub fn write_executable(path: &Path, body: &str) { + let status = std::process::Command::new("/bin/sh") + .args([ + "-c", + r#"printf '%s' "$1" > "$2" && chmod 755 "$2""#, + "sh", + body, + ]) + .arg(path) + .status() + .expect("run /bin/sh"); + assert!(status.success(), "writing {} failed", path.display()); +}