use super::*;
use crate::git_tree::{Agent, AgentState};
use crate::projects::join::JoinState;
fn branch(name: &str, state: AgentState) -> Agent {
Agent {
branch_name: format!("agents/{name}"),
agent_id: name.to_string(),
tip_oid: "0".repeat(40),
tip_short_oid: "0000000".to_string(),
tip_timestamp_unix: 0,
steps: vec![],
preview: None,
streaming_text: None,
tool_calls: vec![],
state,
state_uncertain: false,
pending_messages: 0,
conflicted_oid: None,
budget_oid: None,
abandoned_oid: None,
notify_oid: None,
goal_ball: None,
}
}
#[test]
fn new_prompt_enabled_when_text_present() {
assert!(new_prompt_enabled("hi"));
assert!(new_prompt_enabled(" surrounded by spaces "));
}
#[test]
fn new_prompt_disabled_for_empty_input() {
assert!(!new_prompt_enabled(""));
}
#[test]
fn new_prompt_disabled_for_whitespace_only() {
assert!(!new_prompt_enabled(" \t\n"));
}
#[test]
fn create_ball_enabled_requires_a_nonblank_title() {
assert!(create_ball_enabled("Fix the bug"));
assert!(!create_ball_enabled(""));
assert!(!create_ball_enabled(" \t "));
}
#[test]
fn draft_clears_only_on_a_clean_send() {
use crate::actions::verbs::Outcome;
let clean = Ok(Outcome {
exit: 0,
stdout: String::new(),
stderr: String::new(),
});
let ran_nonzero = Ok(Outcome {
exit: 3,
stdout: String::new(),
stderr: "boom".into(),
});
let never_launched: std::io::Result<Outcome> = Err(std::io::Error::other("no binary"));
assert!(draft_clears(&clean), "a clean send clears the draft");
assert!(
!draft_clears(&ran_nonzero),
"a ran-but-failed verb keeps the draft"
);
assert!(
!draft_clears(&never_launched),
"a spawn failure keeps the draft"
);
}
#[test]
fn stop_disabled_when_selection_is_none() {
let bs = vec![branch("foo", AgentState::InFlight)];
assert!(!stop_enabled(None, &bs));
}
#[test]
fn stop_disabled_when_selection_not_in_agents() {
let bs = vec![branch("foo", AgentState::InFlight)];
assert!(!stop_enabled(Some("bar"), &bs));
}
#[test]
fn stop_disabled_when_selected_agent_stopped() {
let bs = vec![branch("foo", AgentState::Stopped)];
assert!(!stop_enabled(Some("foo"), &bs));
}
#[test]
fn stop_disabled_when_selected_agent_quiescent() {
let bs = vec![branch("foo", AgentState::Quiescent)];
assert!(!stop_enabled(Some("foo"), &bs));
}
#[test]
fn stop_enabled_when_selection_is_in_flight() {
let bs = vec![branch("foo", AgentState::InFlight)];
assert!(stop_enabled(Some("foo"), &bs));
}
#[test]
fn stop_enabled_when_selection_is_live() {
let bs = vec![branch("foo", AgentState::Live)];
assert!(stop_enabled(Some("foo"), &bs));
}
#[test]
fn stop_disabled_when_agents_empty() {
let bs: Vec<Agent> = vec![];
assert!(!stop_enabled(Some("foo"), &bs));
}
#[test]
fn stop_picks_correct_agent_among_several() {
let bs = vec![
branch("a", AgentState::Stopped),
branch("b", AgentState::InFlight),
branch("c", AgentState::Quiescent),
branch("d", AgentState::Live),
];
assert!(stop_enabled(Some("b"), &bs));
assert!(stop_enabled(Some("d"), &bs));
assert!(!stop_enabled(Some("a"), &bs));
assert!(!stop_enabled(Some("c"), &bs));
}
#[test]
fn message_enabled_only_for_a_present_selection_with_text() {
let bs = vec![branch("foo", AgentState::Stopped)];
assert!(message_enabled(Some("foo"), "continue please", &bs));
assert!(!message_enabled(Some("foo"), " ", &bs), "blank text");
assert!(!message_enabled(None, "hi", &bs), "no selection");
assert!(!message_enabled(Some("bar"), "hi", &bs), "absent id");
}
#[test]
fn stop_children_offered_only_with_a_descendant() {
let bs = vec![
branch("root-x", AgentState::Live),
branch("root-x-c1", AgentState::Stopped),
branch("root-y", AgentState::Live),
];
assert!(stop_children_offered("root-x", &bs), "has a child");
assert!(!stop_children_offered("root-y", &bs), "leaf agent");
let bs2 = vec![
branch("root-x", AgentState::Live),
branch("root-xx", AgentState::Live),
];
assert!(!stop_children_offered("root-x", &bs2));
}
#[test]
fn close_enabled_only_for_bound() {
assert!(close_enabled(JoinState::Bound));
assert!(!close_enabled(JoinState::ClaimedElsewhere));
assert!(!close_enabled(JoinState::ReadyStartable));
assert!(!close_enabled(JoinState::Delivered));
}
#[test]
fn unclaim_enabled_only_for_bound() {
assert!(unclaim_enabled(JoinState::Bound));
assert!(!unclaim_enabled(JoinState::ClaimedElsewhere));
assert!(!unclaim_enabled(JoinState::ReadyStartable));
assert!(!unclaim_enabled(JoinState::UnassignedWorkspace));
}
#[test]
fn assign_enabled_only_for_a_ready_ball() {
assert!(assign_enabled(JoinState::ReadyStartable));
assert!(!assign_enabled(JoinState::Bound), "already bound");
assert!(!assign_enabled(JoinState::Blocked));
assert!(!assign_enabled(JoinState::ClaimedElsewhere));
assert!(!assign_enabled(JoinState::Delivered));
assert!(!assign_enabled(JoinState::UnassignedWorkspace));
assert!(!assign_enabled(JoinState::OrphanedProject));
}
#[test]
fn move_enabled_only_for_bound() {
assert!(move_enabled(JoinState::Bound));
assert!(!move_enabled(JoinState::ReadyStartable));
assert!(!move_enabled(JoinState::ClaimedElsewhere));
assert!(!move_enabled(JoinState::Delivered));
}
#[test]
fn actions_state_default_is_empty() {
let s = ActionsState::default();
assert!(s.new_prompt_input.is_empty());
assert!(s.path_dir.is_empty());
assert!(s.selected_branch.is_none());
assert!(!s.stop_children);
assert!(s.last_failure.is_none());
}
#[test]
fn actions_state_clone_eq() {
let s = ActionsState {
new_prompt_input: "hi".to_string(),
selected_branch: Some("b".to_string()),
stop_children: true,
..Default::default()
};
let s2 = s.clone();
assert_eq!(s, s2);
}