use std::fs;
use std::io::Write;
use std::process::{Command, Stdio};
use tempfile::TempDir;
const MX: &str = env!("CARGO_BIN_EXE_mx");
fn mx(dir: &TempDir, args: &[&str], stdin: Option<&str>) -> std::process::Output {
let mut cmd = Command::new(MX);
cmd.args(args)
.env("MX_CURRENT_AGENT", "test-agent")
.env("MX_SURREAL_ROOT", dir.path().join("surreal"))
.env("MX_TRIGGER_FIRED_PATH", dir.path().join("fired.json"))
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped());
let mut child = cmd.spawn().expect("failed to spawn mx");
if let Some(input) = stdin {
child
.stdin
.as_mut()
.unwrap()
.write_all(input.as_bytes())
.unwrap();
}
drop(child.stdin.take());
child.wait_with_output().expect("failed to wait on mx")
}
#[test]
fn empty_message_arg_exits_4() {
let dir = TempDir::new().unwrap();
let out = mx(&dir, &["memory", "trigger-check", ""], Some(""));
assert_eq!(
out.status.code(),
Some(4),
"empty message must exit 4; stderr: {}",
String::from_utf8_lossy(&out.stderr)
);
}
#[test]
fn empty_stdin_no_arg_exits_4() {
let dir = TempDir::new().unwrap();
let out = mx(&dir, &["memory", "trigger-check"], Some(" \n "));
assert_eq!(out.status.code(), Some(4));
}
#[test]
fn no_match_is_success_with_empty_stdout() {
let dir = TempDir::new().unwrap();
let out = mx(
&dir,
&["memory", "trigger-check", "an unremarkable message"],
None,
);
assert!(
out.status.success(),
"no-match must be success (exit 0); stderr: {}",
String::from_utf8_lossy(&out.stderr)
);
assert!(
out.stdout.is_empty(),
"no-match context output must be empty, got: {:?}",
String::from_utf8_lossy(&out.stdout)
);
}
#[test]
fn no_match_json_reports_empty_fired_and_zero_deferred() {
let dir = TempDir::new().unwrap();
let out = mx(
&dir,
&["memory", "trigger-check", "nothing here", "--json"],
None,
);
assert!(out.status.success());
let v: serde_json::Value =
serde_json::from_slice(&out.stdout).expect("trigger-check --json must emit valid JSON");
assert_eq!(v["fired"].as_array().unwrap().len(), 0);
assert_eq!(v["deferred_count"].as_i64().unwrap(), 0);
}
#[test]
fn message_from_stdin_when_arg_absent() {
let dir = TempDir::new().unwrap();
let out = mx(
&dir,
&["memory", "trigger-check"],
Some("a message from stdin"),
);
assert!(
out.status.success(),
"non-empty stdin message must be accepted (exit 0); stderr: {}",
String::from_utf8_lossy(&out.stderr)
);
}
#[test]
fn dry_run_does_not_create_or_modify_fired_state() {
let dir = TempDir::new().unwrap();
let fired = dir.path().join("fired.json");
let out = mx(&dir, &["memory", "trigger-check", "msg", "--dry-run"], None);
assert!(out.status.success());
assert!(
!fired.exists(),
"--dry-run must not create the fired-state file"
);
}
#[test]
fn no_match_real_check_does_not_create_fired_state() {
let dir = TempDir::new().unwrap();
let fired = dir.path().join("fired.json");
let out = mx(
&dir,
&["memory", "trigger-check", "an unremarkable message"],
None,
);
assert!(
out.status.success(),
"no-match must be success (exit 0); stderr: {}",
String::from_utf8_lossy(&out.stderr)
);
assert!(
!fired.exists(),
"no-match real trigger-check must not create the fired-state file"
);
}
#[test]
fn trigger_reset_removes_fired_state_file() {
let dir = TempDir::new().unwrap();
let fired = dir.path().join("fired.json");
fs::write(&fired, r#"{"fired":["kn-aaaaaaaa","kn-bbbbbbbb"]}"#).unwrap();
assert!(fired.exists());
let out = mx(&dir, &["memory", "trigger-reset"], None);
assert!(
out.status.success(),
"trigger-reset must succeed; stderr: {}",
String::from_utf8_lossy(&out.stderr)
);
assert!(
!fired.exists(),
"trigger-reset must remove the fired-state file"
);
}
#[test]
fn trigger_reset_json_reports_path() {
let dir = TempDir::new().unwrap();
let out = mx(&dir, &["memory", "trigger-reset", "--json"], None);
assert!(out.status.success());
let v: serde_json::Value =
serde_json::from_slice(&out.stdout).expect("trigger-reset --json must emit valid JSON");
assert_eq!(v["reset"].as_bool(), Some(true));
assert!(v["path"].as_str().unwrap().contains("fired.json"));
}
#[test]
fn trigger_reset_idempotent_when_no_file() {
let dir = TempDir::new().unwrap();
let out = mx(&dir, &["memory", "trigger-reset"], None);
assert!(out.status.success());
}