use super::*;
use crate::ui_state::SeenKind;
mod doing;
mod expand;
mod flight;
mod name;
mod rows;
mod strip;
fn named_tool(
id: &str,
agent_state: AgentState,
tool: crate::git_tree::ToolCallState,
name: Option<&str>,
start: Option<i64>,
) -> Agent {
let mut a = agent(id, agent_state, 1);
a.tool_calls = vec![crate::git_tree::ToolCall {
tool_id: "toolu_1".into(),
name: name.map(str::to_owned),
start_unix: start,
state: tool,
}];
a
}
fn agent(id: &str, state: AgentState, ts: i64) -> Agent {
Agent {
branch_name: format!("agents/{id}"),
agent_id: id.to_string(),
tip_oid: "a".repeat(40),
tip_short_oid: "aaaaaaaa".into(),
tip_timestamp_unix: ts,
last_action_unix: ts,
messages: 0,
steps: vec![],
preview: None,
stream: crate::git_tree::Stream::default(),
tool_calls: vec![],
state,
state_uncertain: false,
pending: vec![],
conflicted_oid: None,
budget_oid: None,
abandoned_oid: None,
notify_oid: None,
held: None,
goal_ball: None,
name: None,
goal_name: None,
call_start_unix: None,
}
}
fn unseen(_: SeenKind, _: &str, _: &str, _: &str) -> bool {
false
}
fn plain(id: &str) -> ConvBall {
ConvBall {
id: id.to_owned(),
state: None,
title: None,
badge: None,
}
}
#[test]
fn members_returns_the_subtree_in_descent_order() {
let agents = [
agent("r1-0", AgentState::Quiescent, 1),
agent("r1-0-b-1", AgentState::Quiescent, 2),
agent("r1-0-a-1", AgentState::Quiescent, 3),
agent("r2-0", AgentState::Quiescent, 4),
];
let rows = members(&agents, "r1-0");
let ids: Vec<&str> = rows
.iter()
.filter_map(|r| agents.get(r.index))
.map(|a| a.agent_id.as_str())
.collect();
assert_eq!(
ids,
["r1-0", "r1-0-a-1", "r1-0-b-1"],
"root first, children id-sorted"
);
assert_eq!(rows[0].depth, 0);
assert_eq!(rows[1].depth, 1);
assert!(
members(&agents, "r1-0-a-1").is_empty(),
"a child is not a root"
);
assert!(members(&agents, "ghost").is_empty());
}
#[test]
fn root_of_resolves_any_member_to_its_conversation_root() {
let agents = [
agent("r1-0", AgentState::Quiescent, 1),
agent("r1-0-a-1", AgentState::Quiescent, 2),
agent("r2-0", AgentState::Quiescent, 3),
];
assert_eq!(root_of(&agents, "r1-0").as_deref(), Some("r1-0"));
assert_eq!(root_of(&agents, "r1-0-a-1").as_deref(), Some("r1-0"));
assert_eq!(root_of(&agents, "r2-0").as_deref(), Some("r2-0"));
assert_eq!(root_of(&agents, "ghost"), None);
assert_eq!(root_of(&[], "r1-0"), None);
}
#[test]
fn liveness_flags_a_conversation_whose_any_member_holds_a_driver() {
let agents = [
agent("r1-0", AgentState::Quiescent, 10),
agent("r1-0-c-1", AgentState::InFlight, 20),
agent("r2-0", AgentState::Live, 30),
agent("r3-0", AgentState::Stopped, 40),
];
let convs = liveness(&agents);
assert_eq!(
convs,
[
Conversation {
name: "r1-0".to_owned(),
live: true
},
Conversation {
name: "r2-0".to_owned(),
live: true
},
Conversation {
name: "r3-0".to_owned(),
live: false
},
]
);
}
#[test]
fn an_unobservable_probe_counts_as_live() {
let mut agents = [agent("r1-0", AgentState::Quiescent, 10)];
assert!(!liveness(&agents)[0].live);
agents[0].state_uncertain = true;
assert!(liveness(&agents)[0].live);
}
#[test]
fn a_workspace_with_no_agents_has_no_conversations() {
assert!(liveness(&[]).is_empty());
}