use super::*;
#[derive(Clone, Debug, PartialEq, Eq)]
pub(super) struct TestState {
trail: Vec<String>,
agent_visits: u32,
}
fn resolve_durable_target(routes: &[(String, String)], label: &str) -> String {
let target = routes
.iter()
.find(|(l, _)| l == label)
.map(|(_, t)| t.as_str());
match target {
Some(t) if t != crate::language::types::END => t.to_string(),
_ => crate::graph::END.to_string(),
}
}
struct TestFactory;
impl NodeFactory<TestState> for TestFactory {
fn make(&self, spec: &NodeSpec) -> crate::error::Result<BoxedNode<TestState>> {
let name = spec.name.clone();
let routing = spec.routing.clone();
Ok(Arc::new(
move |mut state: TestState, _ctx: NodeContext| -> NodeFuture<TestState> {
let name = name.clone();
let routing = routing.clone();
Box::pin(async move {
state.trail.push(name.clone());
let result = match &routing {
Routing::Terminal | Routing::Next(_) => NodeResult::Update(state),
Routing::Conditional(routes) => {
state.agent_visits += 1;
let label = if state.agent_visits >= 2 {
"final"
} else {
"tool_call"
};
let target = resolve_durable_target(routes, label);
NodeResult::Command(Command::goto([target]).with_update(state))
}
};
Ok(result)
})
},
))
}
}
fn visited_names(run: &crate::graph::GraphExecution<TestState>) -> Vec<String> {
run.visited.iter().map(ToString::to_string).collect()
}
#[tokio::test]
async fn build_graph_runs_to_end() {
let bp = compile(&parse_str(SUPPORT_AGENT).unwrap())
.unwrap()
.remove(0);
let graph = build_graph(&bp, &TestFactory).unwrap();
let run = graph
.run(TestState {
trail: Vec::new(),
agent_visits: 0,
})
.await
.unwrap();
assert_eq!(visited_names(&run), vec!["agent", "tools", "agent"]);
assert_eq!(run.state.trail, vec!["agent", "tools", "agent"]);
assert_eq!(run.state.agent_visits, 2);
}
#[tokio::test]
async fn build_graph_handles_linear_terminal() {
let src = "graph g { start a node a { kind model next b } node b { kind model next END } }";
let bp = compile(&parse_str(src).unwrap()).unwrap().remove(0);
let graph = build_graph(&bp, &TestFactory).unwrap();
let run = graph
.run(TestState {
trail: Vec::new(),
agent_visits: 0,
})
.await
.unwrap();
assert_eq!(visited_names(&run), vec!["a", "b"]);
}