use super::*;
use crate::git_tree::{Agent, AgentState};
use crate::projects::join::JoinState;
mod conversation;
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,
last_action_unix: 0,
messages: 0,
steps: vec![],
preview: None,
stream: crate::git_tree::Stream::default(),
tool_calls: vec![],
state,
state_uncertain: false,
truncated: false,
failure: None,
flagged: None,
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,
}
}
#[test]
fn a_blank_goal_is_not_a_goal_anywhere() {
assert!(goal_present("fix the gate"));
assert!(goal_present(" surrounded by spaces "));
assert!(!goal_present(""));
assert!(!goal_present(" \t\n"));
}
#[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 new_prompt_disabled_when_the_work_directory_does_not_exist() {
let dir = tempfile::tempdir().unwrap();
assert!(new_prompt_enabled("hi", &dir.path().display().to_string()));
assert!(!new_prompt_enabled(
"hi",
&dir.path().join("nope").display().to_string()
));
}
#[test]
fn work_dir_refusal_names_a_directory_that_is_not_there() {
let dir = tempfile::tempdir().unwrap();
assert_eq!(work_dir_refusal(""), None);
assert_eq!(work_dir_refusal(" "), None);
assert_eq!(work_dir_refusal(&dir.path().display().to_string()), None);
let missing = dir.path().join("nonexistent-uat-dir");
assert_eq!(
work_dir_refusal(&format!(" {} ", missing.display())),
Some(format!(
"work directory does not exist: {}",
missing.display()
))
);
let file = dir.path().join("a-file");
std::fs::write(&file, b"x").unwrap();
assert_eq!(
work_dir_refusal(&file.display().to_string()),
Some(format!(
"work directory is not a directory: {}",
file.display()
))
);
}
#[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 new_ball_hints_name_both_boxes() {
let hints = crate::actions::new_ball_hints();
assert_eq!(hints.title, "title");
assert_eq!(hints.body, "body — what done looks like");
}
#[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 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 actions_state_default_is_empty() {
let s = ActionsState::default();
assert!(s.drafts.is_empty());
assert!(s.path_dir.is_empty());
assert!(s.selected_branch.is_none());
assert!(!s.stop_children);
}
#[test]
fn actions_state_clone_eq() {
let s = ActionsState {
drafts: {
let mut d = Drafts::default();
d.set(DraftKey::Message("b".to_string()), "hi".to_string());
d
},
selected_branch: Some("b".to_string()),
stop_children: true,
..Default::default()
};
let s2 = s.clone();
assert_eq!(s, s2);
}