mod address;
mod start_terminal;
use super::*;
use crate::app::Snapshot;
use crate::binding::{Workspace, WorkspaceKind};
use crate::git_tree::{Agent, AgentState, GitTree};
use crate::projects::join::{JoinRow, JoinState};
use crate::start::{BallSpec, Payload, Prepared};
use std::collections::HashMap;
use std::path::Path;
pub(crate) 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,
}
}
pub(crate) fn snapshot(ws: &Path, name: &str, agents: Vec<Agent>, join: Vec<JoinRow>) -> Snapshot {
let mut trees = HashMap::new();
trees.insert(
ws.to_path_buf(),
GitTree {
commits: vec![],
agents,
},
);
let projects: Vec<std::path::PathBuf> = Vec::new();
Snapshot {
workspaces: vec![Workspace {
path: ws.to_path_buf(),
kind: WorkspaceKind::Named {
name: name.to_owned(),
},
}],
projects,
trees,
bills: HashMap::new(),
windows: std::collections::BTreeMap::default(),
balls_by_project: HashMap::new(),
closed_by_project: HashMap::new(),
join_rows: join,
ops: vec![],
growth: vec![],
ui_bytes: None,
derived_at_unix: 0,
cadence: crate::app::Cadence::default(),
fleet: std::collections::BTreeMap::new(),
}
}
pub(crate) fn bound_row(project: &Path, id: &str, ws: &Path, claimant: &str) -> JoinRow {
JoinRow {
project: crate::naming::leaf(project),
ball_id: id.to_owned(),
state: JoinState::Bound,
workspace: Some(crate::naming::leaf(ws)),
claimant: Some(claimant.to_owned()),
title: Some(format!("title of {id}")),
}
}
#[test]
fn the_bl_family_names_its_project_and_nothing_else_does() {
let p = "p".to_owned();
let ws = "ws".to_owned();
let bl_family = [
Action::Close {
project: p.clone(),
id: "b-1".into(),
name: "n".into(),
},
Action::Assign {
project: p.clone(),
id: "b-1".into(),
name: "n".into(),
},
Action::Release {
project: p.clone(),
id: "b-1".into(),
name: "n".into(),
},
Action::Move {
project: p.clone(),
id: "b-1".into(),
from: "a".into(),
to: "b".into(),
},
Action::Create {
project: p.clone(),
title: "t".into(),
name: "n".into(),
body: None,
},
Action::Update {
project: p.clone(),
id: "b-1".into(),
name: "n".into(),
title: None,
body: None,
note: None,
},
];
for action in bl_family {
assert_eq!(action.project(), Some(p.clone()), "{action:?}");
}
let lernie_family = [
Action::Message {
workspace: ws.clone(),
agent: "c".into(),
content: "hi".into(),
},
Action::Stop {
workspace: ws.clone(),
agent: "c".into(),
children: false,
},
Action::Scan {
workspace: ws.clone(),
},
Action::Retarget {
workspace: ws.clone(),
agent: "c".into(),
},
Action::DeleteWorkspace {
workspace: ws.clone(),
typed: "n".into(),
},
Action::DeleteAgent {
workspace: ws.clone(),
agent: "c".into(),
typed: "n".into(),
},
Action::Fork {
workspace: ws.clone(),
parent: "c".into(),
attempt: crate::fork::Attempt::default(),
goal: "g".into(),
},
Action::Ack,
Action::MarkSeen {
workspace: ws.clone(),
agent: "c".into(),
},
Action::ClearTrail,
];
for action in lernie_family {
assert_eq!(action.project(), None, "{action:?}");
}
}
#[test]
fn a_ball_rung_prepare_carries_its_project_and_the_other_rungs_none() {
let p = "proj".to_owned();
let ball = Action::Prepare {
workspace: "ws".to_owned(),
payload: Payload::Ball {
project: p.clone(),
ball: BallSpec::New {
title: "t".into(),
body: String::new(),
},
},
};
assert_eq!(ball.project(), Some(p.clone()));
for payload in [
Payload::Bare,
Payload::Path {
dir: Path::new("/d").to_path_buf(),
},
] {
let a = Action::Prepare {
workspace: "ws".to_owned(),
payload,
};
assert_eq!(a.project(), None, "{a:?}");
}
let prompt = Action::Prompt {
prepared: Prepared {
workspace: "ws".into(),
binding: None,
goal: "g".into(),
origin: crate::opslog::Origin::Conversation,
},
goal: "g".into(),
seed: None,
};
assert_eq!(prompt.project(), None);
}
#[test]
fn the_fan_family_names_its_project() {
let p = "p".to_owned();
let obligation = crate::fan::Obligation {
project: p.clone(),
ball: Some("b-1".into()),
};
for action in [
Action::Fan {
prepared: Prepared {
workspace: "ws".into(),
binding: None,
goal: "g".into(),
origin: crate::opslog::Origin::Balls,
},
obligation: obligation.clone(),
n: 3,
},
Action::Retire {
obligation,
handle: "at-0badcafe".into(),
},
] {
assert_eq!(action.project(), Some(p.clone()), "{action:?}");
}
}