Skip to content

Commit 81023b0

Browse files
authored
Merge pull request #12 from wasabeef/refactor-test
test: Improve worktree command regression tests
2 parents 29d889e + 2b8cceb commit 81023b0

6 files changed

Lines changed: 420 additions & 125 deletions

File tree

tests/custom_path_behavior_test.rs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
88
use anyhow::Result;
99
use git_workers::commands::create_worktree_with_ui;
10+
use std::fs;
11+
use std::process::Command;
1012
use tempfile::TempDir;
1113

1214
mod common;
@@ -270,6 +272,74 @@ fn test_custom_path_with_branch_selection() -> Result<()> {
270272
Ok(())
271273
}
272274

275+
/// Test that tag selection creates a new branch named after the worktree
276+
#[test]
277+
fn test_custom_path_with_tag_selection() -> Result<()> {
278+
let temp_dir = TempDir::new()?;
279+
let test_repo = TestRepo::new(&temp_dir)?;
280+
let manager = test_repo.manager()?;
281+
282+
Command::new("git")
283+
.args(["tag", "v1.2.3"])
284+
.current_dir(test_repo.path())
285+
.output()?;
286+
287+
let ui = TestUI::new()
288+
.with_input("release-preview") // worktree name
289+
.with_selection(1) // custom path option
290+
.with_input("releases/") // directory for tagged releases
291+
.with_selection(2) // select tag
292+
.with_selection(0) // select the first tag
293+
.with_confirmation(false); // don't switch
294+
295+
let result = create_worktree_with_ui(&manager, &ui)?;
296+
assert!(!result);
297+
298+
let worktrees = manager.list_worktrees()?;
299+
let worktree = worktrees
300+
.iter()
301+
.find(|w| w.name == "release-preview")
302+
.expect("release-preview worktree should exist");
303+
304+
assert!(worktree.path.ends_with("releases/release-preview"));
305+
assert_eq!(worktree.branch, "release-preview");
306+
307+
Ok(())
308+
}
309+
310+
/// Test that opting into switch writes the selected path for shell integration
311+
#[test]
312+
fn test_create_worktree_with_switch_writes_switch_file() -> Result<()> {
313+
let temp_dir = TempDir::new()?;
314+
let test_repo = TestRepo::new(&temp_dir)?;
315+
let manager = test_repo.manager()?;
316+
317+
let switch_file = temp_dir.path().join("switch-target.txt");
318+
std::env::set_var("GW_SWITCH_FILE", &switch_file);
319+
320+
let ui = TestUI::new()
321+
.with_input("switch-target") // worktree name
322+
.with_selection(0) // same level as repository
323+
.with_selection(0) // create from HEAD
324+
.with_confirmation(true); // switch to new worktree
325+
326+
let result = create_worktree_with_ui(&manager, &ui)?;
327+
std::env::remove_var("GW_SWITCH_FILE");
328+
329+
assert!(result);
330+
331+
let worktrees = manager.list_worktrees()?;
332+
let worktree = worktrees
333+
.iter()
334+
.find(|w| w.name == "switch-target")
335+
.expect("switch-target worktree should exist");
336+
337+
let switch_target = fs::read_to_string(&switch_file)?;
338+
assert_eq!(switch_target.trim(), worktree.path.to_string_lossy());
339+
340+
Ok(())
341+
}
342+
273343
/// Test UI examples match actual behavior
274344
#[test]
275345
fn test_ui_examples_are_accurate() -> Result<()> {

tests/unit/commands/delete.rs

Lines changed: 76 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,59 @@
44
//! including removal confirmation and cleanup operations.
55
66
use anyhow::Result;
7-
use git_workers::commands::WorktreeDeleteConfig;
7+
use git_workers::commands::{delete_worktree_with_ui, execute_deletion, WorktreeDeleteConfig};
8+
use git_workers::git::GitWorktreeManager;
89
use git_workers::infrastructure::git::WorktreeInfo;
10+
use git_workers::ui::MockUI;
911
use std::fs;
1012
use std::path::PathBuf;
1113
use tempfile::TempDir;
1214

15+
fn setup_test_repo() -> Result<(TempDir, GitWorktreeManager)> {
16+
let temp_dir = TempDir::new()?;
17+
18+
std::process::Command::new("git")
19+
.arg("init")
20+
.current_dir(temp_dir.path())
21+
.output()?;
22+
std::process::Command::new("git")
23+
.args(["config", "user.email", "test@example.com"])
24+
.current_dir(temp_dir.path())
25+
.output()?;
26+
std::process::Command::new("git")
27+
.args(["config", "user.name", "Test User"])
28+
.current_dir(temp_dir.path())
29+
.output()?;
30+
31+
fs::write(temp_dir.path().join("README.md"), "# Test")?;
32+
std::process::Command::new("git")
33+
.arg("add")
34+
.arg("README.md")
35+
.current_dir(temp_dir.path())
36+
.output()?;
37+
std::process::Command::new("git")
38+
.args(["commit", "-m", "Initial commit"])
39+
.current_dir(temp_dir.path())
40+
.output()?;
41+
std::process::Command::new("git")
42+
.args(["branch", "-m", "main"])
43+
.current_dir(temp_dir.path())
44+
.output()?;
45+
46+
let manager = GitWorktreeManager::new_from_path(temp_dir.path())?;
47+
Ok((temp_dir, manager))
48+
}
49+
50+
fn unique_name(prefix: &str) -> String {
51+
format!(
52+
"{prefix}-{}",
53+
std::time::SystemTime::now()
54+
.duration_since(std::time::UNIX_EPOCH)
55+
.unwrap()
56+
.as_nanos()
57+
)
58+
}
59+
1360
#[test]
1461
fn test_worktree_delete_config_creation() {
1562
let config = WorktreeDeleteConfig {
@@ -27,47 +74,39 @@ fn test_worktree_delete_config_creation() {
2774

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

33-
// Initialize repository
34-
std::process::Command::new("git")
35-
.arg("init")
36-
.current_dir(temp_dir.path())
37-
.output()?;
82+
let config = WorktreeDeleteConfig {
83+
name: name.clone(),
84+
path: worktree_path.clone(),
85+
branch: name.clone(),
86+
delete_branch: true,
87+
};
3888

39-
// Create initial commit
40-
fs::write(temp_dir.path().join("README.md"), "# Test")?;
41-
std::process::Command::new("git")
42-
.arg("add")
43-
.arg(".")
44-
.current_dir(temp_dir.path())
45-
.output()?;
46-
std::process::Command::new("git")
47-
.arg("commit")
48-
.arg("-m")
49-
.arg("Initial commit")
50-
.current_dir(temp_dir.path())
51-
.output()?;
89+
execute_deletion(&config, &manager)?;
5290

53-
// Create a worktree
54-
let worktree_path = temp_dir.path().join("../feature");
55-
std::process::Command::new("git")
56-
.args([
57-
"worktree",
58-
"add",
59-
worktree_path.to_str().unwrap(),
60-
"-b",
61-
"feature",
62-
])
63-
.current_dir(temp_dir.path())
64-
.output()?;
91+
assert!(!worktree_path.exists());
92+
93+
let (local_branches, _) = manager.list_all_branches()?;
94+
assert!(!local_branches.contains(&name));
95+
96+
Ok(())
97+
}
98+
99+
#[test]
100+
fn test_delete_worktree_with_ui_cancel_keeps_worktree() -> Result<()> {
101+
let (_temp_dir, manager) = setup_test_repo()?;
102+
let name = unique_name("delete-cancel");
103+
manager.create_worktree_with_new_branch(&name, &name, "main")?;
65104

66-
// Verify worktree exists
67-
assert!(worktree_path.exists());
105+
let ui = MockUI::new().with_selection(0);
106+
delete_worktree_with_ui(&manager, &ui)?;
68107

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

72111
Ok(())
73112
}

tests/unit/commands/mod.rs

Lines changed: 49 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ use anyhow::Result;
1313
use git_workers::commands::{find_config_file_path, get_worktree_icon, validate_custom_path};
1414
use git_workers::constants;
1515
use git_workers::infrastructure::git::{GitWorktreeManager, WorktreeInfo};
16+
use serial_test::serial;
1617
use std::fs;
18+
use std::path::{Path, PathBuf};
1719
use tempfile::TempDir;
1820

1921
// ============================================================================
@@ -45,6 +47,15 @@ fn setup_non_bare_repo() -> Result<(TempDir, GitWorktreeManager)> {
4547
.current_dir(temp_dir.path())
4648
.output()?;
4749

50+
std::process::Command::new("git")
51+
.args(["config", "user.email", "test@example.com"])
52+
.current_dir(temp_dir.path())
53+
.output()?;
54+
std::process::Command::new("git")
55+
.args(["config", "user.name", "Test User"])
56+
.current_dir(temp_dir.path())
57+
.output()?;
58+
4859
// Create initial commit
4960
fs::write(temp_dir.path().join("README.md"), "# Test")?;
5061
std::process::Command::new("git")
@@ -63,6 +74,24 @@ fn setup_non_bare_repo() -> Result<(TempDir, GitWorktreeManager)> {
6374
Ok((temp_dir, manager))
6475
}
6576

77+
struct CurrentDirGuard {
78+
original: PathBuf,
79+
}
80+
81+
impl CurrentDirGuard {
82+
fn change_to(path: &Path) -> Result<Self> {
83+
let original = std::env::current_dir()?;
84+
std::env::set_current_dir(path)?;
85+
Ok(Self { original })
86+
}
87+
}
88+
89+
impl Drop for CurrentDirGuard {
90+
fn drop(&mut self) {
91+
let _ = std::env::set_current_dir(&self.original);
92+
}
93+
}
94+
6695
// ============================================================================
6796
// Icon and Display Tests
6897
// ============================================================================
@@ -149,41 +178,34 @@ fn test_get_worktree_icon_locked() -> Result<()> {
149178
// ============================================================================
150179

151180
#[test]
152-
#[ignore = "Test requires isolated environment to avoid finding project config"]
153-
fn test_find_config_file_path_in_bare_repo() -> Result<()> {
181+
#[serial]
182+
fn test_find_config_file_path_defaults_to_current_bare_repo_directory() -> Result<()> {
154183
let (temp_dir, manager) = setup_test_repo()?;
155-
156-
// Create main worktree
157-
let main_path = temp_dir.path().join("main");
158-
fs::create_dir_all(&main_path)?;
159-
160-
// Create config file in main worktree
161-
let config_path = main_path.join(".git-workers.toml");
162-
fs::write(&config_path, "[worktree]\npattern = \"subdirectory\"")?;
163-
164-
// Update git config to point to main worktree
165-
std::process::Command::new("git")
166-
.args(["config", "core.worktree", main_path.to_str().unwrap()])
167-
.current_dir(temp_dir.path())
168-
.output()?;
184+
let _guard = CurrentDirGuard::change_to(temp_dir.path())?;
169185

170186
let found_path = find_config_file_path(&manager)?;
171-
assert_eq!(found_path, config_path);
187+
assert_eq!(
188+
found_path,
189+
std::env::current_dir()?.join(".git-workers.toml")
190+
);
172191

173192
Ok(())
174193
}
175194

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

181-
// Create config file in repository root
182-
let config_path = temp_dir.path().join(".git-workers.toml");
183-
fs::write(&config_path, "[worktree]\npattern = \"same-level\"")?;
200+
fs::write(
201+
temp_dir.path().join(".git-workers.toml"),
202+
"[worktree]\npattern = \"same-level\"",
203+
)?;
184204

185-
// Create a worktree
186-
let worktree_path = temp_dir.path().join("../feature");
205+
let worktree_path = temp_dir.path().parent().unwrap().join(format!(
206+
"{}-feature",
207+
temp_dir.path().file_name().unwrap().to_string_lossy()
208+
));
187209
std::process::Command::new("git")
188210
.args([
189211
"worktree",
@@ -195,9 +217,11 @@ fn test_find_config_file_path_in_worktree() -> Result<()> {
195217
.current_dir(temp_dir.path())
196218
.output()?;
197219

220+
let worktree_path = worktree_path.canonicalize()?;
221+
let _guard = CurrentDirGuard::change_to(&worktree_path)?;
198222
let manager = GitWorktreeManager::new_from_path(&worktree_path)?;
199223
let found_path = find_config_file_path(&manager)?;
200-
assert_eq!(found_path, config_path);
224+
assert_eq!(found_path, worktree_path.join(".git-workers.toml"));
201225

202226
Ok(())
203227
}

0 commit comments

Comments
 (0)