use std::process::Command;
fn agent_binary() -> String {
let output = Command::new("cargo")
.args(["build", "-p", "terraphim_agent"])
.output()
.expect("cargo build should succeed");
if !output.status.success() {
panic!(
"cargo build failed: {}",
String::from_utf8_lossy(&output.stderr)
);
}
let workspace_root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.unwrap()
.parent()
.unwrap();
workspace_root
.join("target/debug/terraphim-agent")
.to_string_lossy()
.to_string()
}
fn run_procedure_cmd(binary: &str, args: &[&str], env_home: &str) -> (String, String, bool) {
let mut full_args = vec!["learn", "procedure"];
full_args.extend_from_slice(args);
let output = Command::new(binary)
.args(&full_args)
.env("HOME", env_home)
.env("XDG_DATA_HOME", format!("{}/data", env_home))
.output()
.expect("should execute procedure command");
(
String::from_utf8_lossy(&output.stdout).to_string(),
String::from_utf8_lossy(&output.stderr).to_string(),
output.status.success(),
)
}
#[test]
fn procedure_list_empty() {
let binary = agent_binary();
let tmp = tempfile::tempdir().expect("create temp dir");
let home = tmp.path().to_string_lossy().to_string();
let (stdout, _stderr, success) = run_procedure_cmd(&binary, &["list"], &home);
assert!(success, "list on empty store should succeed");
assert!(
stdout.contains("No procedures found"),
"expected empty message, got: {}",
stdout
);
}
#[test]
fn procedure_record_and_show() {
let binary = agent_binary();
let tmp = tempfile::tempdir().expect("create temp dir");
let home = tmp.path().to_string_lossy().to_string();
let (stdout, _stderr, success) = run_procedure_cmd(
&binary,
&[
"record",
"Build Rust project",
"--description",
"Steps to build a Rust project from scratch",
],
&home,
);
assert!(success, "record should succeed");
assert!(
stdout.contains("Created procedure:"),
"expected creation message, got: {}",
stdout
);
let id = stdout
.trim()
.strip_prefix("Created procedure: ")
.expect("should have procedure ID")
.to_string();
let (stdout, _stderr, success) = run_procedure_cmd(&binary, &["show", &id], &home);
assert!(success, "show should succeed");
assert!(stdout.contains("Build Rust project"), "title in output");
assert!(
stdout.contains("Steps to build a Rust project from scratch"),
"description in output"
);
assert!(stdout.contains("Steps (0):"), "zero steps initially");
}
#[test]
fn procedure_add_step_and_list() {
let binary = agent_binary();
let tmp = tempfile::tempdir().expect("create temp dir");
let home = tmp.path().to_string_lossy().to_string();
let (stdout, _, _) = run_procedure_cmd(&binary, &["record", "Deploy app"], &home);
let id = stdout
.trim()
.strip_prefix("Created procedure: ")
.unwrap()
.to_string();
let (stdout, _, success) = run_procedure_cmd(
&binary,
&[
"add-step",
&id,
"cargo build --release",
"--precondition",
"Rust toolchain installed",
"--postcondition",
"Binary exists in target/release",
],
&home,
);
assert!(success, "add-step should succeed");
assert!(stdout.contains("Added step 1"), "first step added");
let (stdout, _, success) = run_procedure_cmd(
&binary,
&["add-step", &id, "scp target/release/app server:/opt/"],
&home,
);
assert!(success, "second add-step should succeed");
assert!(stdout.contains("Added step 2"), "second step added");
let (stdout, _, success) = run_procedure_cmd(&binary, &["show", &id], &home);
assert!(success);
assert!(stdout.contains("Steps (2):"), "two steps");
assert!(stdout.contains("cargo build --release"));
assert!(stdout.contains("pre: Rust toolchain installed"));
assert!(stdout.contains("post: Binary exists in target/release"));
assert!(stdout.contains("scp target/release/app server:/opt/"));
let (stdout, _, success) = run_procedure_cmd(&binary, &["list"], &home);
assert!(success);
assert!(stdout.contains("Deploy app"));
assert!(stdout.contains("2 steps"));
}
#[test]
fn procedure_success_and_failure_update_confidence() {
let binary = agent_binary();
let tmp = tempfile::tempdir().expect("create temp dir");
let home = tmp.path().to_string_lossy().to_string();
let (stdout, _, _) = run_procedure_cmd(&binary, &["record", "Test procedure"], &home);
let id = stdout
.trim()
.strip_prefix("Created procedure: ")
.unwrap()
.to_string();
let (_, _, success) = run_procedure_cmd(&binary, &["success", &id], &home);
assert!(success);
let (_, _, success) = run_procedure_cmd(&binary, &["success", &id], &home);
assert!(success);
let (_, _, success) = run_procedure_cmd(&binary, &["failure", &id], &home);
assert!(success);
let (stdout, _, success) = run_procedure_cmd(&binary, &["show", &id], &home);
assert!(success);
assert!(
stdout.contains("67%"),
"expected 67% confidence, got: {}",
stdout
);
assert!(stdout.contains("2 successes"));
assert!(stdout.contains("1 failures"));
}
#[test]
fn procedure_success_nonexistent_fails() {
let binary = agent_binary();
let tmp = tempfile::tempdir().expect("create temp dir");
let home = tmp.path().to_string_lossy().to_string();
let (_, _stderr, success) = run_procedure_cmd(&binary, &["success", "nonexistent-id"], &home);
assert!(!success, "success on nonexistent procedure should fail");
}
#[test]
fn procedure_replay_dry_run() {
let binary = agent_binary();
let tmp = tempfile::tempdir().expect("create temp dir");
let home = tmp.path().to_string_lossy().to_string();
let (stdout, _, _) = run_procedure_cmd(&binary, &["record", "Echo things"], &home);
let id = stdout
.trim()
.strip_prefix("Created procedure: ")
.unwrap()
.to_string();
run_procedure_cmd(&binary, &["add-step", &id, "echo hello"], &home);
run_procedure_cmd(&binary, &["add-step", &id, "echo world"], &home);
let (stdout, _stderr, success) =
run_procedure_cmd(&binary, &["replay", &id, "--dry-run"], &home);
assert!(success, "dry-run replay should succeed");
assert!(
stdout.contains("[DRY RUN]"),
"should indicate dry run, got: {}",
stdout
);
assert!(
stdout.contains("step 1: OK"),
"step 1 should report OK, got: {}",
stdout
);
assert!(
stdout.contains("step 2: OK"),
"step 2 should report OK, got: {}",
stdout
);
assert!(
stdout.contains("Dry run completed"),
"should report dry run completed, got: {}",
stdout
);
}
#[test]
fn procedure_replay_real_execution() {
let binary = agent_binary();
let tmp = tempfile::tempdir().expect("create temp dir");
let home = tmp.path().to_string_lossy().to_string();
let (stdout, _, _) = run_procedure_cmd(&binary, &["record", "Echo commands"], &home);
let id = stdout
.trim()
.strip_prefix("Created procedure: ")
.unwrap()
.to_string();
run_procedure_cmd(&binary, &["add-step", &id, "echo hello"], &home);
run_procedure_cmd(&binary, &["add-step", &id, "echo world"], &home);
let (stdout, _stderr, success) = run_procedure_cmd(&binary, &["replay", &id], &home);
assert!(success, "replay should succeed, stderr: {}", _stderr);
assert!(
stdout.contains("Replay completed successfully"),
"should report success, got: {}",
stdout
);
let (stdout, _, _) = run_procedure_cmd(&binary, &["show", &id], &home);
assert!(
stdout.contains("1 successes"),
"should show 1 success after replay, got: {}",
stdout
);
}
#[test]
fn procedure_replay_failure_stops_early() {
let binary = agent_binary();
let tmp = tempfile::tempdir().expect("create temp dir");
let home = tmp.path().to_string_lossy().to_string();
let (stdout, _, _) = run_procedure_cmd(&binary, &["record", "Failing procedure"], &home);
let id = stdout
.trim()
.strip_prefix("Created procedure: ")
.unwrap()
.to_string();
run_procedure_cmd(&binary, &["add-step", &id, "false"], &home);
run_procedure_cmd(&binary, &["add-step", &id, "echo should-not-run"], &home);
let (stdout, _stderr, success) = run_procedure_cmd(&binary, &["replay", &id], &home);
assert!(!success, "replay with failure should exit non-zero");
assert!(
stdout.contains("FAILED"),
"should report failure, got: {}",
stdout
);
assert!(
!stdout.contains("step 2: OK"),
"step 2 should not have run, got: {}",
stdout
);
}
#[test]
fn procedure_replay_nonexistent_fails() {
let binary = agent_binary();
let tmp = tempfile::tempdir().expect("create temp dir");
let home = tmp.path().to_string_lossy().to_string();
let (_, _stderr, success) = run_procedure_cmd(&binary, &["replay", "nonexistent-id"], &home);
assert!(!success, "replay of nonexistent procedure should fail");
}
#[test]
fn procedure_health_shows_critical_after_failures() {
let binary = agent_binary();
let tmp = tempfile::tempdir().expect("create temp dir");
let home = tmp.path().to_string_lossy().to_string();
let (stdout, _, _) = run_procedure_cmd(&binary, &["record", "Fragile procedure"], &home);
let id = stdout
.trim()
.strip_prefix("Created procedure: ")
.unwrap()
.to_string();
for _ in 0..5 {
let (_, _, success) = run_procedure_cmd(&binary, &["failure", &id], &home);
assert!(success);
}
let (stdout, _stderr, success) = run_procedure_cmd(&binary, &["health"], &home);
assert!(
success,
"health command should succeed, stderr: {}",
_stderr
);
assert!(
stdout.contains("Critical"),
"expected Critical status, got: {}",
stdout
);
assert!(
stdout.contains("auto-disabled"),
"expected auto-disabled message, got: {}",
stdout
);
}
#[test]
fn procedure_disable_prevents_replay() {
let binary = agent_binary();
let tmp = tempfile::tempdir().expect("create temp dir");
let home = tmp.path().to_string_lossy().to_string();
let (stdout, _, _) = run_procedure_cmd(&binary, &["record", "Disable test"], &home);
let id = stdout
.trim()
.strip_prefix("Created procedure: ")
.unwrap()
.to_string();
run_procedure_cmd(&binary, &["add-step", &id, "echo hello"], &home);
let (stdout, _, success) = run_procedure_cmd(&binary, &["disable", &id], &home);
assert!(success, "disable should succeed");
assert!(
stdout.contains("disabled"),
"expected disabled message, got: {}",
stdout
);
let (_, stderr, success) = run_procedure_cmd(&binary, &["replay", &id], &home);
assert!(!success, "replay of disabled procedure should fail");
assert!(
stderr.contains("disabled"),
"expected disabled error, got stderr: {}",
stderr
);
}
#[test]
fn procedure_enable_allows_replay() {
let binary = agent_binary();
let tmp = tempfile::tempdir().expect("create temp dir");
let home = tmp.path().to_string_lossy().to_string();
let (stdout, _, _) = run_procedure_cmd(&binary, &["record", "Enable test"], &home);
let id = stdout
.trim()
.strip_prefix("Created procedure: ")
.unwrap()
.to_string();
run_procedure_cmd(&binary, &["add-step", &id, "echo hello"], &home);
run_procedure_cmd(&binary, &["disable", &id], &home);
let (stdout, _, success) = run_procedure_cmd(&binary, &["enable", &id], &home);
assert!(success, "enable should succeed");
assert!(
stdout.contains("enabled"),
"expected enabled message, got: {}",
stdout
);
let (stdout, _stderr, success) = run_procedure_cmd(&binary, &["replay", &id], &home);
assert!(
success,
"replay of re-enabled procedure should succeed, stderr: {}",
_stderr
);
assert!(
stdout.contains("Replay completed successfully"),
"expected success message, got: {}",
stdout
);
}