story-kit: merge 128_story_test_coverage_worktree_rs
This commit is contained in:
@@ -306,6 +306,7 @@ async fn run_shell_command(cmd: &str, cwd: &Path) -> Result<(), String> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::config::ComponentConfig;
|
||||
use std::fs;
|
||||
use tempfile::TempDir;
|
||||
|
||||
@@ -437,4 +438,229 @@ mod tests {
|
||||
".story_kit/work/ must still exist in the main checkout"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn branch_name_format() {
|
||||
assert_eq!(branch_name("42_my_story"), "feature/story-42_my_story");
|
||||
assert_eq!(branch_name("1_test"), "feature/story-1_test");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn detect_base_branch_returns_branch_in_git_repo() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let project_root = tmp.path().join("my-project");
|
||||
fs::create_dir_all(&project_root).unwrap();
|
||||
init_git_repo(&project_root);
|
||||
|
||||
let branch = detect_base_branch(&project_root);
|
||||
assert!(!branch.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn detect_base_branch_falls_back_to_master_for_non_git_dir() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let branch = detect_base_branch(tmp.path());
|
||||
assert_eq!(branch, "master");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn configure_sparse_checkout_is_noop() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
assert!(configure_sparse_checkout(tmp.path()).is_ok());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn run_shell_command_succeeds_for_echo() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let result = run_shell_command("echo hello", tmp.path()).await;
|
||||
assert!(result.is_ok(), "Expected success: {:?}", result.err());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn run_shell_command_fails_for_nonzero_exit() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let result = run_shell_command("exit 1", tmp.path()).await;
|
||||
assert!(result.is_err());
|
||||
assert!(result.unwrap_err().contains("failed"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn run_setup_commands_no_components_succeeds() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let config = ProjectConfig {
|
||||
component: vec![],
|
||||
agent: vec![],
|
||||
};
|
||||
assert!(run_setup_commands(tmp.path(), &config).await.is_ok());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn run_setup_commands_runs_each_command_successfully() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let config = ProjectConfig {
|
||||
component: vec![ComponentConfig {
|
||||
name: "test".to_string(),
|
||||
path: ".".to_string(),
|
||||
setup: vec!["echo setup_ok".to_string()],
|
||||
teardown: vec![],
|
||||
}],
|
||||
agent: vec![],
|
||||
};
|
||||
assert!(run_setup_commands(tmp.path(), &config).await.is_ok());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn run_setup_commands_propagates_failure() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let config = ProjectConfig {
|
||||
component: vec![ComponentConfig {
|
||||
name: "test".to_string(),
|
||||
path: ".".to_string(),
|
||||
setup: vec!["exit 1".to_string()],
|
||||
teardown: vec![],
|
||||
}],
|
||||
agent: vec![],
|
||||
};
|
||||
let result = run_setup_commands(tmp.path(), &config).await;
|
||||
assert!(result.is_err(), "Expected failure from failing setup command");
|
||||
assert!(result.unwrap_err().contains("failed"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn run_teardown_commands_ignores_failures() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let config = ProjectConfig {
|
||||
component: vec![ComponentConfig {
|
||||
name: "test".to_string(),
|
||||
path: ".".to_string(),
|
||||
setup: vec![],
|
||||
teardown: vec!["exit 1".to_string()],
|
||||
}],
|
||||
agent: vec![],
|
||||
};
|
||||
// Teardown failures are best-effort — should not propagate
|
||||
assert!(run_teardown_commands(tmp.path(), &config).await.is_ok());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn create_worktree_fresh_creates_dir_and_mcp_json() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let project_root = tmp.path().join("my-project");
|
||||
fs::create_dir_all(&project_root).unwrap();
|
||||
init_git_repo(&project_root);
|
||||
|
||||
let config = ProjectConfig {
|
||||
component: vec![],
|
||||
agent: vec![],
|
||||
};
|
||||
let info = create_worktree(&project_root, "42_fresh_test", &config, 3001)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert!(info.path.exists());
|
||||
assert!(info.path.join(".mcp.json").exists());
|
||||
let mcp = fs::read_to_string(info.path.join(".mcp.json")).unwrap();
|
||||
assert!(mcp.contains("3001"));
|
||||
assert_eq!(info.branch, "feature/story-42_fresh_test");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn create_worktree_reuses_existing_path_and_updates_port() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let project_root = tmp.path().join("my-project");
|
||||
fs::create_dir_all(&project_root).unwrap();
|
||||
init_git_repo(&project_root);
|
||||
|
||||
let config = ProjectConfig {
|
||||
component: vec![],
|
||||
agent: vec![],
|
||||
};
|
||||
// First creation
|
||||
let _info1 = create_worktree(&project_root, "43_reuse_test", &config, 3001)
|
||||
.await
|
||||
.unwrap();
|
||||
// Second call — worktree already exists, reuse path, update port
|
||||
let info2 = create_worktree(&project_root, "43_reuse_test", &config, 3002)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let mcp = fs::read_to_string(info2.path.join(".mcp.json")).unwrap();
|
||||
assert!(mcp.contains("3002"), "MCP json should be updated to new port");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_worktree_sync_cleans_up_directory() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let project_root = tmp.path().join("my-project");
|
||||
fs::create_dir_all(&project_root).unwrap();
|
||||
init_git_repo(&project_root);
|
||||
|
||||
let wt_path = project_root
|
||||
.join(".story_kit")
|
||||
.join("worktrees")
|
||||
.join("test_rm");
|
||||
create_worktree_sync(&project_root, &wt_path, "feature/test-rm").unwrap();
|
||||
assert!(wt_path.exists());
|
||||
|
||||
remove_worktree_sync(&project_root, &wt_path, "feature/test-rm").unwrap();
|
||||
assert!(!wt_path.exists());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn remove_worktree_by_story_id_returns_err_when_not_found() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let config = ProjectConfig {
|
||||
component: vec![],
|
||||
agent: vec![],
|
||||
};
|
||||
|
||||
let result = remove_worktree_by_story_id(tmp.path(), "99_nonexistent", &config).await;
|
||||
assert!(result.is_err());
|
||||
assert!(
|
||||
result
|
||||
.unwrap_err()
|
||||
.contains("Worktree not found for story: 99_nonexistent")
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn remove_worktree_by_story_id_removes_existing_worktree() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let project_root = tmp.path().join("my-project");
|
||||
fs::create_dir_all(&project_root).unwrap();
|
||||
init_git_repo(&project_root);
|
||||
|
||||
let config = ProjectConfig {
|
||||
component: vec![],
|
||||
agent: vec![],
|
||||
};
|
||||
create_worktree(&project_root, "88_remove_by_id", &config, 3001)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let result =
|
||||
remove_worktree_by_story_id(&project_root, "88_remove_by_id", &config).await;
|
||||
assert!(result.is_ok(), "Expected removal to succeed: {:?}", result.err());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn remove_worktree_async_removes_directory() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let project_root = tmp.path().join("my-project");
|
||||
fs::create_dir_all(&project_root).unwrap();
|
||||
init_git_repo(&project_root);
|
||||
|
||||
let config = ProjectConfig {
|
||||
component: vec![],
|
||||
agent: vec![],
|
||||
};
|
||||
let info = create_worktree(&project_root, "77_remove_async", &config, 3001)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let path = info.path.clone();
|
||||
assert!(path.exists());
|
||||
remove_worktree(&project_root, &info, &config).await.unwrap();
|
||||
assert!(!path.exists());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user