use abstractcode::exec::resolve_approval;
use abstractcode::tool_policy::ToolClass;
use serde_json::json;
fn call(name: &str, args: serde_json::Value) -> serde_json::Value {
json!({"name": name, "arguments": args})
}
#[test]
fn deny_reason_names_the_real_tier_rule() {
let batch = vec![call("execute_command", json!({"command": "cargo build"}))];
let r = resolve_approval(&batch, "read", &[], &[]);
assert!(!r.approved, "shell above read level denies");
let reason = r.payload["reason"].as_str().unwrap();
assert!(
reason.contains("permissions 'all'"),
"names the needed level: {reason}"
);
assert!(
reason.contains("accepted level is 'read'"),
"names the accepted level: {reason}"
);
assert!(
reason.contains("--permissions"),
"names the escalation gesture: {reason}"
);
assert_eq!(r.payload["approved_by"].as_str(), Some("policy"));
assert!(r.log.starts_with("DENYING"), "log: {}", r.log);
assert!(
r.log.contains("execute_command"),
"log names tools: {}",
r.log
);
}
#[test]
fn approve_within_level_and_at_the_top_level() {
let reads = vec![call("read_file", json!({"path": "a.rs"}))];
let r = resolve_approval(&reads, "read", &[], &[]);
assert!(r.approved);
assert_eq!(r.payload["approved"].as_bool(), Some(true));
assert_eq!(r.payload["approved_by"].as_str(), Some("policy"));
assert!(r.log.contains("within permissions 'read'"), "{}", r.log);
let shell = vec![call("execute_command", json!({"command": "rm -rf x"}))];
let r = resolve_approval(&shell, "all", &[], &[]);
assert!(r.approved);
assert!(r.log.contains("within permissions 'all'"), "{}", r.log);
}
#[test]
fn git_denies_below_all_client_side_the_refiner_owns_the_proof() {
let batch = vec![call("execute_command", json!({"command": "git status -s"}))];
let r = resolve_approval(&batch, "read", &[], &[]);
assert!(!r.approved, "no client proof: git denies below all");
let push = vec![call("execute_command", json!({"command": "git push"}))];
assert!(!resolve_approval(&push, "read", &[], &[]).approved);
assert!(resolve_approval(&batch, "all", &[], &[]).approved);
}
#[test]
fn ask_override_denies_even_at_permissions_all_naming_the_pin() {
let pins = vec![("read_file".to_string(), "ask".to_string())];
let reads = vec![call("read_file", json!({"path": "a.rs"}))];
let r = resolve_approval(&reads, "all", &pins, &[]);
assert!(!r.approved, "ask pin denies headlessly even at level all");
let reason = r.payload["reason"].as_str().unwrap();
assert!(
reason.contains("read_file") && reason.contains("pinned"),
"the deny names the pin, not a level contradiction: {reason}"
);
}
#[test]
fn unreadable_and_served_disabled_batches_deny_with_honest_reasons() {
let r = resolve_approval(&[], "all", &[], &[]);
assert!(!r.approved, "empty batches fail closed");
assert!(
r.payload["reason"].as_str().unwrap().contains("unreadable"),
"names the real cause: {}",
r.payload
);
let classes = vec![ToolClass {
name: "send_email".into(),
approval: Some("auto".into()),
served_disabled: true,
enable_gate: "ABSTRACT_ENABLE_COMMS_TOOLS".into(),
..Default::default()
}];
let batch = vec![call("send_email", json!({"to": "x@y.z"}))];
let r = resolve_approval(&batch, "all", &[], &classes);
assert!(!r.approved, "served-disabled denies even at all");
let reason = r.payload["reason"].as_str().unwrap();
assert!(
reason.contains("ABSTRACT_ENABLE_COMMS_TOOLS"),
"the deny names the gate: {reason}"
);
}
#[test]
fn server_truth_is_preferred_over_the_name_table() {
let classes = vec![ToolClass {
name: "mcp::search".into(),
approval: Some("auto".into()),
tier: Some("tier2_world".into()),
..Default::default()
}];
let batch = vec![call("mcp::search", json!({"q": "x"}))];
assert!(
resolve_approval(&batch, "read", &[], &classes).approved,
"server approval:auto lifts an mcp tool to read tier"
);
assert!(!resolve_approval(&batch, "read", &[], &[]).approved);
}
#[test]
fn exit_codes_match_the_documented_truth_table() {
use abstractcode::exec::exit_code_for_status;
assert_eq!(exit_code_for_status("completed"), 0);
assert_eq!(exit_code_for_status("cancelled"), 130);
assert_eq!(exit_code_for_status("failed"), 1);
assert_eq!(exit_code_for_status("unknown"), 1);
assert_eq!(exit_code_for_status(""), 1);
}
#[test]
fn explicit_missing_workflow_refuses_instead_of_silently_substituting() {
use abstractcode::exec::explicit_workflow_mismatch;
use abstractcode::store::Workflow;
let wf = |b: &str, f: &str| Workflow {
bundle_id: b.into(),
flow_id: f.into(),
name: String::new(),
description: String::new(),
};
let catalog = vec![wf("basic-agent", "main"), wf("coding-agent", "coder")];
let fallback = catalog[0].clone();
let msg = explicit_workflow_mismatch("definitely-not-a-workflow", &fallback, &catalog)
.expect("mismatch must refuse");
assert!(msg.contains("definitely-not-a-workflow"), "{msg}");
assert!(
msg.contains("coding-agent:coder"),
"lists the catalog: {msg}"
);
assert!(
explicit_workflow_mismatch("coding-agent:nope", &fallback, &catalog).is_some(),
"bundle:flow must match both halves"
);
assert!(explicit_workflow_mismatch("coding-agent:coder", &catalog[1], &catalog).is_none());
assert!(
explicit_workflow_mismatch("coding-agent", &catalog[1], &catalog).is_none(),
"bundle alone accepts any flow within it"
);
}
#[test]
fn tier_decisions_from_the_name_table_carry_the_fallback_label() {
let reads = vec![call("read_file", json!({"path": "a.rs"}))];
let r = resolve_approval(&reads, "read", &[], &[]);
assert!(r.approved);
assert!(
r.log.contains("#FALLBACK") && r.log.contains("read_file"),
"name-table classification is labeled: {}",
r.log
);
let shell = vec![call("execute_command", json!({"command": "cargo build"}))];
let denied = resolve_approval(&shell, "read", &[], &[]);
assert!(!denied.approved);
assert!(denied.log.contains("#FALLBACK"), "{}", denied.log);
}
#[test]
fn server_facts_and_explicit_user_acts_never_carry_the_fallback_label() {
let classes = vec![ToolClass {
name: "read_file".into(),
approval: Some("auto".into()),
tier: Some("tier2_world".into()),
..Default::default()
}];
let reads = vec![call("read_file", json!({"path": "a.rs"}))];
let r = resolve_approval(&reads, "read", &[], &classes);
assert!(r.approved);
assert!(
!r.log.contains("#FALLBACK"),
"server-classified decisions are unlabeled: {}",
r.log
);
let r = resolve_approval(&reads, "all", &[], &[]);
assert!(r.approved);
assert!(
r.log.contains("#FALLBACK"),
"name-table classification stays labeled at every level: {}",
r.log
);
let pins = vec![("read_file".to_string(), "ask".to_string())];
let r = resolve_approval(&reads, "all", &pins, &[]);
assert!(!r.approved);
assert!(
!r.log.contains("#FALLBACK"),
"pin denials carry no classification label: {}",
r.log
);
let git = vec![call("execute_command", json!({"command": "git status -s"}))];
let r = resolve_approval(&git, "read", &[], &[]);
assert!(!r.approved);
assert!(
r.log.contains("#FALLBACK"),
"name-table git classification is labeled: {}",
r.log
);
}