Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions tests/custom_path_behavior_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

use anyhow::Result;
use git_workers::commands::create_worktree_with_ui;
use std::fs;
use std::process::Command;
use tempfile::TempDir;

mod common;
Expand Down Expand Up @@ -270,6 +272,74 @@ fn test_custom_path_with_branch_selection() -> Result<()> {
Ok(())
}

/// Test that tag selection creates a new branch named after the worktree
#[test]
fn test_custom_path_with_tag_selection() -> Result<()> {
let temp_dir = TempDir::new()?;
let test_repo = TestRepo::new(&temp_dir)?;
let manager = test_repo.manager()?;

Command::new("git")
.args(["tag", "v1.2.3"])
.current_dir(test_repo.path())
.output()?;

let ui = TestUI::new()
.with_input("release-preview") // worktree name
.with_selection(1) // custom path option
.with_input("releases/") // directory for tagged releases
.with_selection(2) // select tag
.with_selection(0) // select the first tag
.with_confirmation(false); // don't switch

let result = create_worktree_with_ui(&manager, &ui)?;
assert!(!result);

let worktrees = manager.list_worktrees()?;
let worktree = worktrees
.iter()
.find(|w| w.name == "release-preview")
.expect("release-preview worktree should exist");

assert!(worktree.path.ends_with("releases/release-preview"));
assert_eq!(worktree.branch, "release-preview");

Ok(())
}

/// Test that opting into switch writes the selected path for shell integration
#[test]
fn test_create_worktree_with_switch_writes_switch_file() -> Result<()> {
let temp_dir = TempDir::new()?;
let test_repo = TestRepo::new(&temp_dir)?;
let manager = test_repo.manager()?;

let switch_file = temp_dir.path().join("switch-target.txt");
std::env::set_var("GW_SWITCH_FILE", &switch_file);

let ui = TestUI::new()
.with_input("switch-target") // worktree name
.with_selection(0) // same level as repository
.with_selection(0) // create from HEAD
.with_confirmation(true); // switch to new worktree

let result = create_worktree_with_ui(&manager, &ui)?;
std::env::remove_var("GW_SWITCH_FILE");

assert!(result);

let worktrees = manager.list_worktrees()?;
let worktree = worktrees
.iter()
.find(|w| w.name == "switch-target")
.expect("switch-target worktree should exist");

let switch_target = fs::read_to_string(&switch_file)?;
assert_eq!(switch_target.trim(), worktree.path.to_string_lossy());

Ok(())
}

/// Test UI examples match actual behavior
#[test]
fn test_ui_examples_are_accurate() -> Result<()> {
Expand Down
113 changes: 76 additions & 37 deletions tests/unit/commands/delete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,59 @@
//! including removal confirmation and cleanup operations.

use anyhow::Result;
use git_workers::commands::WorktreeDeleteConfig;
use git_workers::commands::{delete_worktree_with_ui, execute_deletion, WorktreeDeleteConfig};
use git_workers::git::GitWorktreeManager;
use git_workers::infrastructure::git::WorktreeInfo;
use git_workers::ui::MockUI;
use std::fs;
use std::path::PathBuf;
use tempfile::TempDir;

fn setup_test_repo() -> Result<(TempDir, GitWorktreeManager)> {
let temp_dir = TempDir::new()?;

std::process::Command::new("git")
.arg("init")
.current_dir(temp_dir.path())
.output()?;
std::process::Command::new("git")
.args(["config", "user.email", "test@example.com"])
.current_dir(temp_dir.path())
.output()?;
std::process::Command::new("git")
.args(["config", "user.name", "Test User"])
.current_dir(temp_dir.path())
.output()?;

fs::write(temp_dir.path().join("README.md"), "# Test")?;
std::process::Command::new("git")
.arg("add")
.arg("README.md")
.current_dir(temp_dir.path())
.output()?;
std::process::Command::new("git")
.args(["commit", "-m", "Initial commit"])
.current_dir(temp_dir.path())
.output()?;
std::process::Command::new("git")
.args(["branch", "-m", "main"])
.current_dir(temp_dir.path())
.output()?;

let manager = GitWorktreeManager::new_from_path(temp_dir.path())?;
Ok((temp_dir, manager))
}

fn unique_name(prefix: &str) -> String {
format!(
"{prefix}-{}",
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos()
)
}

#[test]
fn test_worktree_delete_config_creation() {
let config = WorktreeDeleteConfig {
Expand All @@ -27,47 +74,39 @@ fn test_worktree_delete_config_creation() {

// Integration test for actual deletion
#[test]
fn test_worktree_deletion_simulation() -> Result<()> {
let temp_dir = TempDir::new()?;
fn test_execute_deletion_removes_worktree_and_branch() -> Result<()> {
let (_temp_dir, manager) = setup_test_repo()?;
let name = unique_name("delete-me");
let worktree_path = manager.create_worktree_with_new_branch(&name, &name, "main")?;

// Initialize repository
std::process::Command::new("git")
.arg("init")
.current_dir(temp_dir.path())
.output()?;
let config = WorktreeDeleteConfig {
name: name.clone(),
path: worktree_path.clone(),
branch: name.clone(),
delete_branch: true,
};

// Create initial commit
fs::write(temp_dir.path().join("README.md"), "# Test")?;
std::process::Command::new("git")
.arg("add")
.arg(".")
.current_dir(temp_dir.path())
.output()?;
std::process::Command::new("git")
.arg("commit")
.arg("-m")
.arg("Initial commit")
.current_dir(temp_dir.path())
.output()?;
execute_deletion(&config, &manager)?;

// Create a worktree
let worktree_path = temp_dir.path().join("../feature");
std::process::Command::new("git")
.args([
"worktree",
"add",
worktree_path.to_str().unwrap(),
"-b",
"feature",
])
.current_dir(temp_dir.path())
.output()?;
assert!(!worktree_path.exists());

let (local_branches, _) = manager.list_all_branches()?;
assert!(!local_branches.contains(&name));

Ok(())
}

#[test]
fn test_delete_worktree_with_ui_cancel_keeps_worktree() -> Result<()> {
let (_temp_dir, manager) = setup_test_repo()?;
let name = unique_name("delete-cancel");
manager.create_worktree_with_new_branch(&name, &name, "main")?;

// Verify worktree exists
assert!(worktree_path.exists());
let ui = MockUI::new().with_selection(0);
delete_worktree_with_ui(&manager, &ui)?;

// Test would validate deletion target if API was public
// assert!(validate_deletion_target("feature").is_ok());
let worktrees = manager.list_worktrees()?;
assert!(worktrees.iter().any(|w| w.name == name));

Ok(())
}
Expand Down
74 changes: 49 additions & 25 deletions tests/unit/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ use anyhow::Result;
use git_workers::commands::{find_config_file_path, get_worktree_icon, validate_custom_path};
use git_workers::constants;
use git_workers::infrastructure::git::{GitWorktreeManager, WorktreeInfo};
use serial_test::serial;
use std::fs;
use std::path::{Path, PathBuf};
use tempfile::TempDir;

// ============================================================================
Expand Down Expand Up @@ -45,6 +47,15 @@ fn setup_non_bare_repo() -> Result<(TempDir, GitWorktreeManager)> {
.current_dir(temp_dir.path())
.output()?;

std::process::Command::new("git")
.args(["config", "user.email", "test@example.com"])
.current_dir(temp_dir.path())
.output()?;
std::process::Command::new("git")
.args(["config", "user.name", "Test User"])
.current_dir(temp_dir.path())
.output()?;

// Create initial commit
fs::write(temp_dir.path().join("README.md"), "# Test")?;
std::process::Command::new("git")
Expand All @@ -63,6 +74,24 @@ fn setup_non_bare_repo() -> Result<(TempDir, GitWorktreeManager)> {
Ok((temp_dir, manager))
}

struct CurrentDirGuard {
original: PathBuf,
}

impl CurrentDirGuard {
fn change_to(path: &Path) -> Result<Self> {
let original = std::env::current_dir()?;
std::env::set_current_dir(path)?;
Ok(Self { original })
}
}

impl Drop for CurrentDirGuard {
fn drop(&mut self) {
let _ = std::env::set_current_dir(&self.original);
}
}

// ============================================================================
// Icon and Display Tests
// ============================================================================
Expand Down Expand Up @@ -149,41 +178,34 @@ fn test_get_worktree_icon_locked() -> Result<()> {
// ============================================================================

#[test]
#[ignore = "Test requires isolated environment to avoid finding project config"]
fn test_find_config_file_path_in_bare_repo() -> Result<()> {
#[serial]
fn test_find_config_file_path_defaults_to_current_bare_repo_directory() -> Result<()> {
let (temp_dir, manager) = setup_test_repo()?;

// Create main worktree
let main_path = temp_dir.path().join("main");
fs::create_dir_all(&main_path)?;

// Create config file in main worktree
let config_path = main_path.join(".git-workers.toml");
fs::write(&config_path, "[worktree]\npattern = \"subdirectory\"")?;

// Update git config to point to main worktree
std::process::Command::new("git")
.args(["config", "core.worktree", main_path.to_str().unwrap()])
.current_dir(temp_dir.path())
.output()?;
let _guard = CurrentDirGuard::change_to(temp_dir.path())?;

let found_path = find_config_file_path(&manager)?;
assert_eq!(found_path, config_path);
assert_eq!(
found_path,
std::env::current_dir()?.join(".git-workers.toml")
);

Ok(())
}

#[test]
#[ignore = "Test requires isolated environment to avoid finding project config"]
fn test_find_config_file_path_in_worktree() -> Result<()> {
#[serial]
fn test_find_config_file_path_prefers_current_worktree_directory() -> Result<()> {
let (temp_dir, _manager) = setup_non_bare_repo()?;

// Create config file in repository root
let config_path = temp_dir.path().join(".git-workers.toml");
fs::write(&config_path, "[worktree]\npattern = \"same-level\"")?;
fs::write(
temp_dir.path().join(".git-workers.toml"),
"[worktree]\npattern = \"same-level\"",
)?;

// Create a worktree
let worktree_path = temp_dir.path().join("../feature");
let worktree_path = temp_dir.path().parent().unwrap().join(format!(
"{}-feature",
temp_dir.path().file_name().unwrap().to_string_lossy()
));
std::process::Command::new("git")
.args([
"worktree",
Expand All @@ -195,9 +217,11 @@ fn test_find_config_file_path_in_worktree() -> Result<()> {
.current_dir(temp_dir.path())
.output()?;

let worktree_path = worktree_path.canonicalize()?;
let _guard = CurrentDirGuard::change_to(&worktree_path)?;
let manager = GitWorktreeManager::new_from_path(&worktree_path)?;
let found_path = find_config_file_path(&manager)?;
assert_eq!(found_path, config_path);
assert_eq!(found_path, worktree_path.join(".git-workers.toml"));

Ok(())
}
Expand Down
Loading
Loading