use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::sync::atomic::{AtomicU32, Ordering};
static COUNTER: AtomicU32 = AtomicU32::new(0);
struct Run {
stdout: String,
stderr: String,
success: bool,
}
struct Sandbox {
base: PathBuf,
mind_home: PathBuf,
claude_home: PathBuf,
}
impl Sandbox {
fn new() -> Sandbox {
let n = COUNTER.fetch_add(1, Ordering::SeqCst);
let base = std::env::temp_dir().join(format!("mind-badcfg-{}-{n}", std::process::id()));
let _ = std::fs::remove_dir_all(&base);
std::fs::create_dir_all(base.join("mind")).unwrap();
Sandbox {
mind_home: base.join("mind"),
claude_home: base.join("claude"),
base,
}
}
fn run(&self, args: &[&str]) -> Run {
let out = Command::new(env!("CARGO_BIN_EXE_mind"))
.args(args)
.env("MIND_HOME", &self.mind_home)
.env("CLAUDE_HOME", &self.claude_home)
.env_remove("MIND_AGENT_HOMES")
.env_remove("MIND_POLICY_FILE")
.env_remove("MIND_MAX_METADATA_SIZE")
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.output()
.expect("run mind");
Run {
stdout: String::from_utf8_lossy(&out.stdout).into_owned(),
stderr: String::from_utf8_lossy(&out.stderr).into_owned(),
success: out.status.success(),
}
}
fn config(&self, body: &str) {
write(&self.mind_home.join("config.toml"), body);
}
}
impl Drop for Sandbox {
fn drop(&mut self) {
let _ = std::fs::remove_dir_all(&self.base);
}
}
fn write(path: &Path, contents: &str) {
std::fs::create_dir_all(path.parent().unwrap()).unwrap();
std::fs::write(path, contents).unwrap();
}
#[test]
fn malformed_config_without_cap_key_does_not_fail_cap_resolution() {
let sb = Sandbox::new();
sb.config("this is = = not toml\n");
let r = sb.run(&["hooks", "list", "nonexistent-target"]);
assert!(
!r.stderr.contains("config.toml"),
"cap resolution must not surface the config error: {}{}",
r.stdout,
r.stderr
);
}
#[test]
fn malformed_config_naming_the_cap_key_still_errors() {
let sb = Sandbox::new();
sb.config("max-metadata-size = = 1MiB\n");
let r = sb.run(&["hooks", "list", "nonexistent-target"]);
assert!(!r.success, "{}{}", r.stdout, r.stderr);
assert!(r.stderr.contains("config"), "{}", r.stderr);
}
#[test]
fn flag_outranks_a_malformed_config_naming_the_cap_key() {
let sb = Sandbox::new();
sb.config("max-metadata-size = = 1MiB\n");
let r = sb.run(&[
"--max-metadata-size",
"1MiB",
"hooks",
"list",
"nonexistent-target",
]);
assert!(
!r.stderr.contains("config.toml"),
"{}{}",
r.stdout,
r.stderr
);
}