treeflow 0.2.1

CLI tool for simplified Git worktree management to speed up switching contexts when working collaboratively.
Documentation
#[cfg(test)]
mod utils;

#[cfg(test)]
mod start_work_tests {
    use crate::utils::git_command::GitCommand;
    use crate::utils::treeflow_command::TreeflowCommand;
    use predicates::prelude::predicate;
    use std::fs;
    use std::path::PathBuf;
    use tempdir::TempDir;
    use treeflow::utils::Return;

    const WORK_NAME: &str = "test-feature";
    const WORK_TYPE: &str = "feature";
    const WORK_TYPE_PREFIX: &str = "feature/cp/";
    const EXISTING_BRANCH: &str = "feature/cp/existing";

    fn setup_config(config_dir_buf: &PathBuf, repository_dir_buf: &PathBuf, worktrees_dir_buf: &PathBuf) {
        // Add worktype to config
        TreeflowCommand::new(config_dir_buf.clone())
            .worktype_add(WORK_TYPE, WORK_TYPE_PREFIX)
            .current_dir(&repository_dir_buf)
            .cmd()
            .assert()
            .success();

        // Add project to config
        TreeflowCommand::new(config_dir_buf.clone())
            .project_add(&repository_dir_buf, Some(&worktrees_dir_buf))
            .current_dir(&repository_dir_buf)
            .cmd()
            .assert()
            .success();
    }

    #[test]
    fn start_creates_new_branch_and_worktree() {
        let config_dir = TempDir::new("config_dir").expect("should be able to create temp config dir");
        let repository_dir = TempDir::new("repository_dir").expect("should be able to create temp repository dir");
        let worktrees_dir = TempDir::new("worktrees_dir").expect("should be able to create temp worktrees dir");

        let config_dir_buf = config_dir.path().to_path_buf();
        let repository_dir_buf = repository_dir.path().to_path_buf();
        let worktrees_dir_buf = worktrees_dir.path().to_path_buf();

        let expected_worktree_path = worktrees_dir_buf.join(WORK_TYPE_PREFIX).join(WORK_NAME);
        let expected_branch_name = format!("{}{}", WORK_TYPE_PREFIX, WORK_NAME);

        setup_config(&config_dir_buf, &repository_dir_buf, &worktrees_dir_buf);

        GitCommand::init(&repository_dir_buf);

        TreeflowCommand::new(config_dir_buf)
            .current_dir(&repository_dir_buf)
            .start(WORK_NAME, WORK_TYPE)
            .cmd()
            .assert()
            .success()
            .stdout(Return::Cd { path: expected_worktree_path.clone() }.print());

        GitCommand::is_on_branch(&expected_worktree_path, &expected_branch_name);
    }

    #[test]
    fn start_uses_existing_branch() {
        let config_dir = TempDir::new("config_dir").expect("should be able to create temp config dir");
        let repository_dir = TempDir::new("repository_dir").expect("should be able to create temp repository dir");
        let worktrees_dir = TempDir::new("worktrees_dir").expect("should be able to create temp worktrees dir");

        let config_dir_buf = config_dir.path().to_path_buf();
        let repository_dir_buf = repository_dir.path().to_path_buf();
        let worktrees_dir_buf = worktrees_dir.path().to_path_buf();

        setup_config(&config_dir_buf, &repository_dir_buf, &worktrees_dir_buf);

        GitCommand::init(&repository_dir_buf);
        GitCommand::create_branch(&repository_dir_buf, EXISTING_BRANCH);

        // Execute start command with existing branch name
        let work_name = EXISTING_BRANCH.strip_prefix(WORK_TYPE_PREFIX).unwrap();
        let expected_worktree_path = worktrees_dir_buf.join(EXISTING_BRANCH);
        let expected_branch_name = format!("{}{}", WORK_TYPE_PREFIX, WORK_NAME);

        TreeflowCommand::new(config_dir_buf)
            .current_dir(&repository_dir_buf)
            .start(work_name, WORK_TYPE)
            .cmd()
            .assert()
            .success()
            .stdout(Return::Cd { path: expected_worktree_path.clone() }.print());

        GitCommand::is_on_branch(&expected_worktree_path, &expected_branch_name);
    }

    #[test]
    fn start_uses_existing_worktree() {
        let config_dir = TempDir::new("config_dir").expect("should be able to create temp config dir");
        let repository_dir = TempDir::new("repository_dir").expect("should be able to create temp repository dir");
        let worktrees_dir = TempDir::new("worktrees_dir").expect("should be able to create temp worktrees dir");

        let config_dir_buf = config_dir.path().to_path_buf();
        let repository_dir_buf = repository_dir.path().to_path_buf();
        let worktrees_dir_buf = worktrees_dir.path().to_path_buf();

        let test_branch_worktree_dir_buf = &worktrees_dir_buf.join(EXISTING_BRANCH);

        setup_config(&config_dir_buf, &repository_dir_buf, &worktrees_dir_buf);

        GitCommand::init(&repository_dir_buf);
        GitCommand::create_worktree(&repository_dir_buf, EXISTING_BRANCH, test_branch_worktree_dir_buf);

        // Execute start command with existing branch name
        let work_name = EXISTING_BRANCH.strip_prefix(WORK_TYPE_PREFIX).unwrap();
        let expected_worktree_path = worktrees_dir_buf.join(EXISTING_BRANCH);
        let expected_branch_name = format!("{}{}", WORK_TYPE_PREFIX, WORK_NAME);

        TreeflowCommand::new(config_dir_buf)
            .current_dir(&repository_dir_buf)
            .start(work_name, WORK_TYPE)
            .cmd()
            .assert()
            .success()
            .stdout(Return::Cd { path: expected_worktree_path.clone() }.print());

        GitCommand::is_on_branch(&test_branch_worktree_dir_buf, &expected_branch_name);
    }

    #[test]
    fn fails_when_worktree_dir_exists_but_is_not_valid() {
        let config_dir = TempDir::new("config_dir").expect("should be able to create temp config dir");
        let repository_dir = TempDir::new("repository_dir").expect("should be able to create temp repository dir");
        let worktrees_dir = TempDir::new("worktrees_dir").expect("should be able to create temp worktrees dir");

        let config_dir_buf = config_dir.path().to_path_buf();
        let repository_dir_buf = repository_dir.path().to_path_buf();
        let worktrees_dir_buf = worktrees_dir.path().to_path_buf();

        setup_config(&config_dir_buf, &repository_dir_buf, &worktrees_dir_buf);

        GitCommand::init(&repository_dir_buf);

        // Execute start command with existing branch name
        let work_name = EXISTING_BRANCH.strip_prefix(WORK_TYPE_PREFIX).unwrap();
        let expected_worktree_path = worktrees_dir_buf.join(EXISTING_BRANCH);

        fs::create_dir_all(&expected_worktree_path).expect("Failed to create empty worktree directory");

        TreeflowCommand::new(&config_dir_buf)
            .current_dir(&repository_dir_buf)
            .start(work_name, WORK_TYPE)
            .cmd()
            .assert()
            .failure()
            .stderr(predicate::str::contains(expected_worktree_path.to_str().expect("Conversion of worktree path failed")));
    }
}