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 review_tests {
    use std::fs;
    use tempdir::TempDir;
    use predicates::prelude::*;
    use treeflow::utils::Return;
    use crate::utils::git_command::GitCommand;
    use crate::utils::treeflow_command::TreeflowCommand;

    const TEST_BRANCH: &str = "feature/test";

    #[test]
    fn review_existing_branch_creates_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();

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

        // 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();

        // Review an existing branch
        let expected_worktree_path = worktrees_dir_buf.join(TEST_BRANCH);

        TreeflowCommand::new(config_dir_buf)
            .current_dir(&repository_dir_buf)
            .review(TEST_BRANCH)
            .cmd()
            .assert()
            .success()
            .stdout(Return::Cd { path: expected_worktree_path }.print());
    }

    #[test]
    fn review_navigates_to_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(TEST_BRANCH);

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

        // 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();

        // Review should navigate to existing worktree
        TreeflowCommand::new(config_dir_buf)
            .current_dir(&repository_dir_buf)
            .review(TEST_BRANCH)
            .cmd()
            .assert()
            .success()
            .stdout(Return::Cd { path: test_branch_worktree_dir_buf.clone() }.print());
    }

    /// Tests that the review command fails gracefully when not in a project directory
    #[test]
    fn fails_when_not_in_project() {
        let config_dir = TempDir::new("config_dir").expect("should be able to create temp config dir");
        let outside_dir = TempDir::new("outside_dir").expect("should be able to create temp outside dir");

        let outside_dir_buf = outside_dir.path().to_path_buf();

        TreeflowCommand::new(config_dir.path().to_path_buf())
            .review(TEST_BRANCH)
            .current_dir(&outside_dir_buf)
            .cmd()
            .assert()
            .failure()
            .stderr(predicate::str::contains("not found").and(predicate::str::contains(outside_dir_buf.display().to_string())));
    }

    #[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();

        let expected_worktree_path = &worktrees_dir_buf.join(TEST_BRANCH);

        GitCommand::init(&repository_dir_buf);

        // 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();

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

        // Review command should succeed by removing the empty directory and creating a proper worktree
        TreeflowCommand::new(config_dir_buf)
            .current_dir(&repository_dir_buf)
            .review(TEST_BRANCH)
            .cmd()
            .assert()
            .failure()
            .stderr(predicate::str::contains(expected_worktree_path.to_str().expect("Conversion of worktree path failed")));
    }
}