use areev_cal::AreevFacade;
use areev_core::authz::{AuthzSet, Grant, Verb};
use areev_core::types::{Grain, Tool, ToolKind};
use areev_loop::{ReadOpts, SubstrateRead};
use areev_loop_adapter::{BorrowedSubstrate, AreevSubstrate};
use areev_store::Areev;
use serde_json::json;
const NOW: i64 = 1_700_000_000_000;
fn open_temp() -> (tempfile::TempDir, Areev) {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("agent.db");
let store = Areev::open(path.to_str().unwrap()).unwrap();
(dir, store)
}
fn definition(name: &str, evalset: Option<&str>) -> Tool {
let mut t = Tool::new(name)
.kind(ToolKind::Definition)
.tool_description("a tool")
.namespace("caller");
if let Some(e) = evalset {
t.common
.extra_fields
.insert("evalset_hash".into(), json!(e));
}
t
}
#[test]
fn a_runnable_plan_validates() {
let (_d, store) = open_temp();
let sub = AreevSubstrate::new(store, None);
let plan = json!({
"nodes": ["fetch", "score", "post"],
"edges": [
{"src": "fetch", "dst": "score"},
{"src": "score", "dst": "post", "cond": "ok == true"},
],
"bindings": {"fetch": "aa".repeat(32)},
"retries": {"fetch": 2},
});
sub.validate_plan(&plan).expect("a walkable graph validates");
}
#[test]
fn an_unreachable_node_is_refused() {
let (_d, store) = open_temp();
let sub = AreevSubstrate::new(store, None);
let plan = json!({
"nodes": ["fetch", "score", "orphan"],
"edges": [{"src": "fetch", "dst": "score"}],
});
let err = sub.validate_plan(&plan).unwrap_err().to_string();
assert!(
err.contains("not runnable"),
"the refusal must say the plan is the problem: {err}"
);
}
#[test]
fn an_unbounded_cycle_is_refused() {
let (_d, store) = open_temp();
let sub = AreevSubstrate::new(store, None);
let plan = json!({
"nodes": ["a", "b"],
"edges": [
{"src": "a", "dst": "b"},
{"src": "b", "dst": "a"},
],
});
assert!(sub.validate_plan(&plan).is_err(), "unbounded cycle");
let bounded = json!({
"nodes": ["a", "b"],
"edges": [
{"src": "a", "dst": "b"},
{"src": "b", "dst": "a", "max_cycles": 3},
],
});
sub.validate_plan(&bounded).expect("a bounded cycle is runnable");
}
#[test]
fn an_unparsable_condition_is_refused() {
let (_d, store) = open_temp();
let sub = AreevSubstrate::new(store, None);
let plan = json!({
"nodes": ["a", "b"],
"edges": [{"src": "a", "dst": "b", "cond": "ok ===== maybe"}],
});
assert!(sub.validate_plan(&plan).is_err());
}
#[test]
fn a_malformed_edge_fails_instead_of_being_skipped() {
let (_d, store) = open_temp();
let sub = AreevSubstrate::new(store, None);
for bad in [
json!({"src": "a"}), json!({"dst": "b"}), json!({"src": "a", "dst": "b", "cond": 7}), json!({"src": "a", "dst": "b", "max_cycles": "many"}),
] {
let plan = json!({"nodes": ["a", "b"], "edges": [bad.clone()]});
let err = sub.validate_plan(&plan).unwrap_err().to_string();
assert!(
err.contains("edge"),
"a malformed edge must be refused as an edge, not silently \
dropped into a different graph — got {err} for {bad}"
);
}
}
#[test]
fn a_body_that_is_not_a_workflow_is_refused() {
let (_d, store) = open_temp();
let sub = AreevSubstrate::new(store, None);
for body in [
json!("nodes"),
json!([1, 2]),
json!({}), json!({"nodes": "fetch"}), json!({"nodes": [1]}), json!({"nodes": ["a"], "edges": {}}), json!({"nodes": ["a"], "bindings": []}), json!({"nodes": ["a"], "bindings": {"a": 3}}),
json!({"nodes": ["a"], "retries": []}),
json!({"nodes": ["a"], "retries": {"a": "lots"}}),
] {
assert!(
sub.validate_plan(&body).is_err(),
"must refuse {body}"
);
}
}
#[test]
fn the_pin_comes_from_the_tools_live_definition() {
let (_d, mut store) = open_temp();
store.add(&definition("screen", Some("beef"))).unwrap();
let sub = AreevSubstrate::new(store, None);
assert_eq!(sub.tool_evalset("screen").unwrap().as_deref(), Some("beef"));
}
#[test]
fn a_tool_that_declares_no_evalset_has_no_pin() {
let (_d, mut store) = open_temp();
store.add(&definition("triage", None)).unwrap();
store.add(&definition("release", Some(" "))).unwrap();
let sub = AreevSubstrate::new(store, None);
assert_eq!(sub.tool_evalset("triage").unwrap(), None);
assert_eq!(sub.tool_evalset("release").unwrap(), None);
assert_eq!(sub.tool_evalset("no_such_tool").unwrap(), None);
}
#[test]
fn an_execution_record_never_supplies_the_pin() {
let (_d, mut store) = open_temp();
let mut call = Tool::new("screen")
.content("matched")
.is_error(false)
.namespace("caller");
call.common
.extra_fields
.insert("evalset_hash".into(), json!("attacker-chosen"));
store.add(&call).unwrap();
let sub = AreevSubstrate::new(store, None);
assert_eq!(
sub.tool_evalset("screen").unwrap(),
None,
"an execution grain is not a declaration"
);
}
#[test]
fn superseding_the_definition_moves_the_pin() {
let (_d, mut store) = open_temp();
let first = store
.add(&definition("screen", Some("aaaa")).created_at(NOW - 1_000))
.unwrap();
let mut newer = definition("screen", Some("bbbb")).created_at(NOW);
store.supersede(&first, &mut newer).unwrap();
let sub = AreevSubstrate::new(store, None);
assert_eq!(sub.tool_evalset("screen").unwrap().as_deref(), Some("bbbb"));
}
#[test]
fn a_session_without_the_grant_resolves_no_pin() {
let (_d, mut store) = open_temp();
store.add(&definition("screen", Some("beef"))).unwrap();
let facade = AreevFacade::with_session(store, Some("areev-loop".into()), None);
facade.bind(AuthzSet::restricted(
"agent:scoped",
vec![Grant {
verbs: vec![Verb::Read, Verb::LoopRun],
namespaces: vec!["areev-loop".into()],
}],
));
let sub = BorrowedSubstrate::new(&facade);
assert_eq!(
sub.tool_evalset("screen").unwrap(),
None,
"an unreadable definition must never yield its pin"
);
assert!(sub
.grains_of_type("tool", Some("caller"), ReadOpts::default())
.is_err());
}
#[test]
fn the_adapter_declares_the_plan_and_code_capabilities() {
let (_d, store) = open_temp();
let sub = AreevSubstrate::new(store, None);
let caps = sub.capabilities();
assert!(caps.plans, "plans");
assert!(caps.code, "code");
assert!(sub
.grains_of_type("tool", None, ReadOpts::default())
.is_ok());
}