huskies: merge 770
This commit is contained in:
@@ -104,13 +104,6 @@ pub mod test_helpers {
|
||||
std::fs::create_dir_all(tmp.path().join(".huskies")).unwrap();
|
||||
}
|
||||
|
||||
/// Create the `5_done` and `6_archived` work-stage directories.
|
||||
pub fn make_work_dirs(tmp: &TempDir) {
|
||||
for stage in &["5_done", "6_archived"] {
|
||||
std::fs::create_dir_all(tmp.path().join(".huskies").join("work").join(stage)).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// Create all six pipeline stage directories under `.huskies/work/`.
|
||||
pub fn make_stage_dirs(tmp: &TempDir) {
|
||||
for stage in &[
|
||||
|
||||
@@ -113,20 +113,6 @@ pub async fn stop_agent(
|
||||
.map_err(Error::AgentNotFound)
|
||||
}
|
||||
|
||||
/// List all agents, optionally filtering out those belonging to archived stories.
|
||||
///
|
||||
/// When `project_root` is `None` the archive filter is skipped and all agents
|
||||
/// are returned (safe default when the server is not yet fully configured).
|
||||
pub fn list_agents(pool: &AgentPool, project_root: Option<&Path>) -> Result<Vec<AgentInfo>, Error> {
|
||||
let agents = pool.list_agents().map_err(Error::Io)?;
|
||||
match project_root {
|
||||
Some(root) => Ok(selection::filter_non_archived(agents, |id| {
|
||||
io::is_archived(root, id)
|
||||
})),
|
||||
None => Ok(agents),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a git worktree for a story.
|
||||
pub async fn create_worktree(
|
||||
pool: &AgentPool,
|
||||
@@ -289,50 +275,9 @@ fn config_to_entries(config: &ProjectConfig) -> Vec<AgentConfigEntry> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::agents::AgentStatus;
|
||||
use io::test_helpers::*;
|
||||
use std::sync::Arc;
|
||||
use tempfile::TempDir;
|
||||
|
||||
fn make_pool(tmp: &TempDir) -> Arc<AgentPool> {
|
||||
let (tx, _) = tokio::sync::broadcast::channel(64);
|
||||
let pool = AgentPool::new(3001, tx);
|
||||
let state = crate::state::SessionState::default();
|
||||
*state.project_root.lock().unwrap() = Some(tmp.path().to_path_buf());
|
||||
Arc::new(pool)
|
||||
}
|
||||
|
||||
// ── list_agents ───────────────────────────────────────────────────────────
|
||||
|
||||
#[tokio::test]
|
||||
async fn list_agents_excludes_archived_stories() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
make_work_dirs(&tmp);
|
||||
write_story_file(
|
||||
&tmp,
|
||||
".huskies/work/6_archived/79_story_archived.md",
|
||||
"---\nname: archived\n---\n",
|
||||
);
|
||||
|
||||
let pool = make_pool(&tmp);
|
||||
pool.inject_test_agent("79_story_archived", "coder-1", AgentStatus::Completed);
|
||||
pool.inject_test_agent("80_story_active", "coder-1", AgentStatus::Running);
|
||||
|
||||
let agents = list_agents(&pool, Some(tmp.path())).unwrap();
|
||||
assert!(!agents.iter().any(|a| a.story_id == "79_story_archived"));
|
||||
assert!(agents.iter().any(|a| a.story_id == "80_story_active"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn list_agents_includes_all_when_no_project_root() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let pool = make_pool(&tmp);
|
||||
pool.inject_test_agent("42_story_whatever", "coder-1", AgentStatus::Completed);
|
||||
|
||||
let agents = list_agents(&pool, None).unwrap();
|
||||
assert!(agents.iter().any(|a| a.story_id == "42_story_whatever"));
|
||||
}
|
||||
|
||||
// ── get_agent_config ──────────────────────────────────────────────────────
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -4,22 +4,6 @@
|
||||
//! return a result without touching the filesystem, network, or any mutable
|
||||
//! global state. This makes them fast to test without tempdirs or async runtimes.
|
||||
use crate::agent_log::LogEntry;
|
||||
use crate::agents::AgentInfo;
|
||||
|
||||
/// Filter a list of agents, removing any whose story is archived.
|
||||
///
|
||||
/// `is_archived` is a predicate injected by the caller — typically a closure
|
||||
/// over the project root that calls `io::is_archived`. This keeps the function
|
||||
/// pure: it never touches the filesystem itself.
|
||||
pub fn filter_non_archived<F>(agents: Vec<AgentInfo>, is_archived: F) -> Vec<AgentInfo>
|
||||
where
|
||||
F: Fn(&str) -> bool,
|
||||
{
|
||||
agents
|
||||
.into_iter()
|
||||
.filter(|info| !is_archived(&info.story_id))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Concatenate the text of all `output` events from an agent log.
|
||||
///
|
||||
@@ -42,22 +26,6 @@ pub fn collect_output_text(entries: &[LogEntry]) -> String {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::agents::AgentStatus;
|
||||
|
||||
fn make_agent(story_id: &str) -> AgentInfo {
|
||||
AgentInfo {
|
||||
story_id: story_id.to_string(),
|
||||
agent_name: "coder-1".to_string(),
|
||||
status: AgentStatus::Running,
|
||||
session_id: None,
|
||||
worktree_path: None,
|
||||
base_branch: None,
|
||||
completion: None,
|
||||
log_session_id: None,
|
||||
throttled: false,
|
||||
termination_reason: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn make_log_entry(event_type: &str, text: Option<&str>) -> LogEntry {
|
||||
let mut obj = serde_json::Map::new();
|
||||
@@ -74,51 +42,6 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
// ── filter_non_archived ───────────────────────────────────────────────────
|
||||
|
||||
#[test]
|
||||
fn filter_keeps_non_archived_agents() {
|
||||
let agents = vec![make_agent("10_active"), make_agent("11_active")];
|
||||
let result = filter_non_archived(agents, |_| false);
|
||||
assert_eq!(result.len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn filter_removes_archived_agents() {
|
||||
let agents = vec![make_agent("10_archived"), make_agent("11_active")];
|
||||
let result = filter_non_archived(agents, |id| id == "10_archived");
|
||||
assert_eq!(result.len(), 1);
|
||||
assert_eq!(result[0].story_id, "11_active");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn filter_removes_all_when_all_archived() {
|
||||
let agents = vec![make_agent("10_a"), make_agent("11_b")];
|
||||
let result = filter_non_archived(agents, |_| true);
|
||||
assert!(result.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn filter_returns_empty_for_empty_input() {
|
||||
let result = filter_non_archived(vec![], |_| false);
|
||||
assert!(result.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn filter_preserves_order() {
|
||||
let agents = vec![
|
||||
make_agent("1_a"),
|
||||
make_agent("2_b"),
|
||||
make_agent("3_c"),
|
||||
make_agent("4_d"),
|
||||
];
|
||||
let result = filter_non_archived(agents, |id| id == "2_b");
|
||||
assert_eq!(result.len(), 3);
|
||||
assert_eq!(result[0].story_id, "1_a");
|
||||
assert_eq!(result[1].story_id, "3_c");
|
||||
assert_eq!(result[2].story_id, "4_d");
|
||||
}
|
||||
|
||||
// ── collect_output_text ───────────────────────────────────────────────────
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user