story-kit: merge 108_story_test_coverage_http_agents_rs_to_70
This commit is contained in:
@@ -379,4 +379,307 @@ mod tests {
|
||||
let result = api.list_agents().await.unwrap().0;
|
||||
assert!(result.iter().any(|a| a.story_id == "42_story_whatever"));
|
||||
}
|
||||
|
||||
fn make_project_toml(root: &path::Path, content: &str) {
|
||||
let sk_dir = root.join(".story_kit");
|
||||
std::fs::create_dir_all(&sk_dir).unwrap();
|
||||
std::fs::write(sk_dir.join("project.toml"), content).unwrap();
|
||||
}
|
||||
|
||||
// --- get_agent_config tests ---
|
||||
|
||||
#[tokio::test]
|
||||
async fn get_agent_config_returns_default_when_no_toml() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let ctx = AppContext::new_test(tmp.path().to_path_buf());
|
||||
let api = AgentsApi {
|
||||
ctx: Arc::new(ctx),
|
||||
};
|
||||
let result = api.get_agent_config().await.unwrap().0;
|
||||
// Default config has one agent named "default"
|
||||
assert_eq!(result.len(), 1);
|
||||
assert_eq!(result[0].name, "default");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn get_agent_config_returns_configured_agents() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
make_project_toml(
|
||||
tmp.path(),
|
||||
r#"
|
||||
[[agent]]
|
||||
name = "coder-1"
|
||||
role = "Full-stack engineer"
|
||||
model = "sonnet"
|
||||
max_turns = 30
|
||||
max_budget_usd = 5.0
|
||||
|
||||
[[agent]]
|
||||
name = "qa"
|
||||
role = "QA reviewer"
|
||||
model = "haiku"
|
||||
"#,
|
||||
);
|
||||
let ctx = AppContext::new_test(tmp.path().to_path_buf());
|
||||
let api = AgentsApi {
|
||||
ctx: Arc::new(ctx),
|
||||
};
|
||||
let result = api.get_agent_config().await.unwrap().0;
|
||||
assert_eq!(result.len(), 2);
|
||||
assert_eq!(result[0].name, "coder-1");
|
||||
assert_eq!(result[0].role, "Full-stack engineer");
|
||||
assert_eq!(result[0].model, Some("sonnet".to_string()));
|
||||
assert_eq!(result[0].max_turns, Some(30));
|
||||
assert_eq!(result[0].max_budget_usd, Some(5.0));
|
||||
assert_eq!(result[1].name, "qa");
|
||||
assert_eq!(result[1].model, Some("haiku".to_string()));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn get_agent_config_returns_error_when_no_project_root() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let ctx = AppContext::new_test(tmp.path().to_path_buf());
|
||||
*ctx.state.project_root.lock().unwrap() = None;
|
||||
let api = AgentsApi {
|
||||
ctx: Arc::new(ctx),
|
||||
};
|
||||
let result = api.get_agent_config().await;
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
// --- reload_config tests ---
|
||||
|
||||
#[tokio::test]
|
||||
async fn reload_config_returns_default_when_no_toml() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let ctx = AppContext::new_test(tmp.path().to_path_buf());
|
||||
let api = AgentsApi {
|
||||
ctx: Arc::new(ctx),
|
||||
};
|
||||
let result = api.reload_config().await.unwrap().0;
|
||||
assert_eq!(result.len(), 1);
|
||||
assert_eq!(result[0].name, "default");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn reload_config_returns_configured_agents() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
make_project_toml(
|
||||
tmp.path(),
|
||||
r#"
|
||||
[[agent]]
|
||||
name = "supervisor"
|
||||
role = "Coordinator"
|
||||
model = "opus"
|
||||
allowed_tools = ["Read", "Bash"]
|
||||
"#,
|
||||
);
|
||||
let ctx = AppContext::new_test(tmp.path().to_path_buf());
|
||||
let api = AgentsApi {
|
||||
ctx: Arc::new(ctx),
|
||||
};
|
||||
let result = api.reload_config().await.unwrap().0;
|
||||
assert_eq!(result.len(), 1);
|
||||
assert_eq!(result[0].name, "supervisor");
|
||||
assert_eq!(result[0].role, "Coordinator");
|
||||
assert_eq!(result[0].model, Some("opus".to_string()));
|
||||
assert_eq!(
|
||||
result[0].allowed_tools,
|
||||
Some(vec!["Read".to_string(), "Bash".to_string()])
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn reload_config_returns_error_when_no_project_root() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let ctx = AppContext::new_test(tmp.path().to_path_buf());
|
||||
*ctx.state.project_root.lock().unwrap() = None;
|
||||
let api = AgentsApi {
|
||||
ctx: Arc::new(ctx),
|
||||
};
|
||||
let result = api.reload_config().await;
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
// --- list_worktrees tests ---
|
||||
|
||||
#[tokio::test]
|
||||
async fn list_worktrees_returns_empty_when_no_worktree_dir() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let ctx = AppContext::new_test(tmp.path().to_path_buf());
|
||||
let api = AgentsApi {
|
||||
ctx: Arc::new(ctx),
|
||||
};
|
||||
let result = api.list_worktrees().await.unwrap().0;
|
||||
assert!(result.is_empty());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn list_worktrees_returns_entries_from_dir() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let worktrees_dir = tmp.path().join(".story_kit").join("worktrees");
|
||||
std::fs::create_dir_all(worktrees_dir.join("42_story_foo")).unwrap();
|
||||
std::fs::create_dir_all(worktrees_dir.join("43_story_bar")).unwrap();
|
||||
|
||||
let ctx = AppContext::new_test(tmp.path().to_path_buf());
|
||||
let api = AgentsApi {
|
||||
ctx: Arc::new(ctx),
|
||||
};
|
||||
let mut result = api.list_worktrees().await.unwrap().0;
|
||||
result.sort_by(|a, b| a.story_id.cmp(&b.story_id));
|
||||
|
||||
assert_eq!(result.len(), 2);
|
||||
assert_eq!(result[0].story_id, "42_story_foo");
|
||||
assert_eq!(result[1].story_id, "43_story_bar");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn list_worktrees_returns_error_when_no_project_root() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let ctx = AppContext::new_test(tmp.path().to_path_buf());
|
||||
*ctx.state.project_root.lock().unwrap() = None;
|
||||
let api = AgentsApi {
|
||||
ctx: Arc::new(ctx),
|
||||
};
|
||||
let result = api.list_worktrees().await;
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
// --- stop_agent tests ---
|
||||
|
||||
#[tokio::test]
|
||||
async fn stop_agent_returns_error_when_no_project_root() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let ctx = AppContext::new_test(tmp.path().to_path_buf());
|
||||
*ctx.state.project_root.lock().unwrap() = None;
|
||||
let api = AgentsApi {
|
||||
ctx: Arc::new(ctx),
|
||||
};
|
||||
let result = api
|
||||
.stop_agent(Json(StopAgentPayload {
|
||||
story_id: "42_story_foo".to_string(),
|
||||
agent_name: "coder-1".to_string(),
|
||||
}))
|
||||
.await;
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn stop_agent_returns_error_when_agent_not_found() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let ctx = AppContext::new_test(tmp.path().to_path_buf());
|
||||
let api = AgentsApi {
|
||||
ctx: Arc::new(ctx),
|
||||
};
|
||||
let result = api
|
||||
.stop_agent(Json(StopAgentPayload {
|
||||
story_id: "nonexistent_story".to_string(),
|
||||
agent_name: "coder-1".to_string(),
|
||||
}))
|
||||
.await;
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn stop_agent_succeeds_with_running_agent() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let ctx = AppContext::new_test(tmp.path().to_path_buf());
|
||||
ctx.agents
|
||||
.inject_test_agent("42_story_foo", "coder-1", AgentStatus::Running);
|
||||
let api = AgentsApi {
|
||||
ctx: Arc::new(ctx),
|
||||
};
|
||||
let result = api
|
||||
.stop_agent(Json(StopAgentPayload {
|
||||
story_id: "42_story_foo".to_string(),
|
||||
agent_name: "coder-1".to_string(),
|
||||
}))
|
||||
.await
|
||||
.unwrap()
|
||||
.0;
|
||||
assert!(result);
|
||||
}
|
||||
|
||||
// --- start_agent error path ---
|
||||
|
||||
#[tokio::test]
|
||||
async fn start_agent_returns_error_when_no_project_root() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let ctx = AppContext::new_test(tmp.path().to_path_buf());
|
||||
*ctx.state.project_root.lock().unwrap() = None;
|
||||
let api = AgentsApi {
|
||||
ctx: Arc::new(ctx),
|
||||
};
|
||||
let result = api
|
||||
.start_agent(Json(StartAgentPayload {
|
||||
story_id: "42_story_foo".to_string(),
|
||||
agent_name: None,
|
||||
}))
|
||||
.await;
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
// --- create_worktree error path ---
|
||||
|
||||
#[tokio::test]
|
||||
async fn create_worktree_returns_error_when_no_project_root() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let ctx = AppContext::new_test(tmp.path().to_path_buf());
|
||||
*ctx.state.project_root.lock().unwrap() = None;
|
||||
let api = AgentsApi {
|
||||
ctx: Arc::new(ctx),
|
||||
};
|
||||
let result = api
|
||||
.create_worktree(Json(CreateWorktreePayload {
|
||||
story_id: "42_story_foo".to_string(),
|
||||
}))
|
||||
.await;
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn create_worktree_returns_error_when_not_a_git_repo() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
// project_root is set but has no git repo — git worktree add will fail
|
||||
let ctx = AppContext::new_test(tmp.path().to_path_buf());
|
||||
let api = AgentsApi {
|
||||
ctx: Arc::new(ctx),
|
||||
};
|
||||
let result = api
|
||||
.create_worktree(Json(CreateWorktreePayload {
|
||||
story_id: "42_story_foo".to_string(),
|
||||
}))
|
||||
.await;
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
// --- remove_worktree error paths ---
|
||||
|
||||
#[tokio::test]
|
||||
async fn remove_worktree_returns_error_when_no_project_root() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let ctx = AppContext::new_test(tmp.path().to_path_buf());
|
||||
*ctx.state.project_root.lock().unwrap() = None;
|
||||
let api = AgentsApi {
|
||||
ctx: Arc::new(ctx),
|
||||
};
|
||||
let result = api
|
||||
.remove_worktree(Path("42_story_foo".to_string()))
|
||||
.await;
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn remove_worktree_returns_error_when_worktree_not_found() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
// project_root is set but no worktree exists for this story_id
|
||||
let ctx = AppContext::new_test(tmp.path().to_path_buf());
|
||||
let api = AgentsApi {
|
||||
ctx: Arc::new(ctx),
|
||||
};
|
||||
let result = api
|
||||
.remove_worktree(Path("nonexistent_story".to_string()))
|
||||
.await;
|
||||
assert!(result.is_err());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user