#![cfg(all(unix, feature = "workflow"))]
mod common;
use std::process::{Command, Stdio};
fn run(cfg_text: &str) -> (Option<i32>, String) {
let dir = common::unique_path("acting", "d");
std::fs::create_dir_all(&dir).unwrap();
let cfg = format!("{dir}/c.yaml");
std::fs::write(&cfg, cfg_text.replace("__STATE__", &format!("{dir}/state"))).unwrap();
let out = Command::new(env!("CARGO_BIN_EXE_agentd"))
.args(["--config", &cfg])
.stdin(Stdio::null())
.stdout(Stdio::null())
.output()
.expect("run");
let log = String::from_utf8_lossy(&out.stderr).to_string();
let _ = std::fs::remove_dir_all(&dir);
(out.status.code(), log)
}
const BASE: &str = "config_version: \"1\"\n\
store: { kind: file, file: { path: __STATE__ } }\n\
observability: { log_level: info, log_content: true }\n\
lifecycle: { run_until: idle, idle_grace: 2s }\n";
const WF: &str = "workflows:\n\
\x20 - name: w\n steps:\n\
\x20 s: { kind: once }\n\
\x20 f: { kind: finish, depends_on: [s], status: completed, output: done }\n";
#[test]
fn autonomous_work_is_attributed_to_a_named_actor() {
let (code, log) = run(&format!(
"{BASE}agent: {{ name: a }}\nidentity: {{ autonomous_as: \"system:scheduler\" }}\n{WF}"
));
assert_eq!(code, Some(0), "{log}");
assert!(
log.contains("\"acting_for\":\"system:scheduler\""),
"the trigger should have named its actor\n{log}"
);
}
#[test]
fn autonomous_work_has_an_actor_without_configuration() {
let (code, log) = run(&format!("{BASE}agent: {{ name: a }}\n{WF}"));
assert_eq!(code, Some(0), "{log}");
assert!(
log.contains("\"acting_for\":\"system\""),
"autonomous work should default to a named actor\n{log}"
);
}
#[test]
fn labels_are_declared_by_the_operator_not_derived_from_input() {
let (code, log) = run(&format!(
"{BASE}agent: {{ name: a }}\n\
identity: {{ autonomous_as: \"system\", labels: {{ tenant: acme, cost_center: CC-42 }} }}\n{WF}"
));
assert_eq!(code, Some(0), "{log}");
let (bad, blog) = run(&format!(
"{BASE}agent: {{ name: a }}\nidentity: {{ nonsense: 1 }}\n{WF}"
));
assert_eq!(
bad,
Some(2),
"an unknown identity field should be refused\n{blog}"
);
}
#[test]
fn a_principal_rate_quota_is_accepted_and_bound_to_the_principal() {
let (code, log) = run(&format!(
"{BASE}agent: {{ name: a }}\n\
a2a:\n principals:\n\
\x20 - match: {{ sub: \"*@acme.example\" }}\n role: user\n\
\x20 labels: {{ tenant: acme }}\n\
\x20 quotas: {{ rate: \"30/1m\", budget: {{ windows: [{{ per: day, tokens: 200000 }}] }} }}\n{WF}"
));
assert_eq!(code, Some(0), "the quota block should be accepted\n{log}");
}
#[test]
fn a_malformed_rate_quota_is_refused_at_startup() {
let (code, log) = run(&format!(
"{BASE}agent: {{ name: a }}\n\
a2a:\n principals:\n\
\x20 - match: {{ any: true }}\n role: user\n\
\x20 quotas: {{ rate: \"not-a-rate\" }}\n{WF}"
));
assert_eq!(code, Some(2), "{log}");
}