Skip to content

Commit 014820d

Browse files
author
Test User
committed
test: add common test modules and custom path behavior tests
- Add reusable test repository and UI mock utilities - Implement comprehensive custom path worktree creation tests - Improve test code organization and reduce duplication - Enhance test coverage for edge cases and error scenarios
1 parent 5fb695c commit 014820d

File tree

4 files changed

+673
-0
lines changed

4 files changed

+673
-0
lines changed

tests/common/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
//! Common test utilities
2+
3+
mod test_repo;
4+
mod test_ui;
5+
6+
pub use test_repo::TestRepo;
7+
pub use test_ui::TestUI;

tests/common/test_repo.rs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
//! Test repository setup utilities
2+
3+
use anyhow::Result;
4+
use git2::{Repository, Signature};
5+
use git_workers::git::GitWorktreeManager;
6+
use std::fs;
7+
use std::path::{Path, PathBuf};
8+
use tempfile::TempDir;
9+
10+
/// A test repository with helper methods
11+
pub struct TestRepo {
12+
path: PathBuf,
13+
repo: Repository,
14+
}
15+
16+
impl TestRepo {
17+
/// Create a new test repository
18+
pub fn new(temp_dir: &TempDir) -> Result<Self> {
19+
let path = temp_dir.path().join("test-repo");
20+
fs::create_dir_all(&path)?;
21+
22+
let repo = Repository::init(&path)?;
23+
24+
// Create initial commit
25+
let sig = Signature::now("Test User", "test@example.com")?;
26+
let tree_id = {
27+
let mut index = repo.index()?;
28+
index.write()?;
29+
index.write_tree()?
30+
};
31+
32+
let tree = repo.find_tree(tree_id)?;
33+
repo.commit(Some("HEAD"), &sig, &sig, "Initial commit", &tree, &[])?;
34+
35+
// Drop tree before moving repo
36+
drop(tree);
37+
38+
Ok(Self { path, repo })
39+
}
40+
41+
/// Get the repository path
42+
pub fn path(&self) -> &Path {
43+
&self.path
44+
}
45+
46+
/// Create a GitWorktreeManager for this test repository
47+
pub fn manager(&self) -> Result<GitWorktreeManager> {
48+
GitWorktreeManager::new_from_path(&self.path)
49+
}
50+
51+
/// Create a new branch
52+
pub fn create_branch(&self, name: &str) -> Result<()> {
53+
let head = self.repo.head()?.target().unwrap();
54+
let commit = self.repo.find_commit(head)?;
55+
self.repo.branch(name, &commit, false)?;
56+
Ok(())
57+
}
58+
59+
/// Create a new commit
60+
#[allow(dead_code)]
61+
pub fn create_commit(&self, message: &str) -> Result<()> {
62+
let sig = Signature::now("Test User", "test@example.com")?;
63+
let tree_id = {
64+
let mut index = self.repo.index()?;
65+
index.write()?;
66+
index.write_tree()?
67+
};
68+
69+
let tree = self.repo.find_tree(tree_id)?;
70+
let parent = self.repo.head()?.target().unwrap();
71+
let parent_commit = self.repo.find_commit(parent)?;
72+
73+
self.repo
74+
.commit(Some("HEAD"), &sig, &sig, message, &tree, &[&parent_commit])?;
75+
76+
Ok(())
77+
}
78+
}

tests/common/test_ui.rs

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
//! Test implementation of UserInterface for testing
2+
3+
use anyhow::Result;
4+
use git_workers::ui::UserInterface;
5+
use std::sync::{Arc, Mutex};
6+
7+
/// Test implementation of UserInterface that provides pre-programmed responses
8+
#[derive(Clone)]
9+
pub struct TestUI {
10+
inputs: Arc<Mutex<Vec<String>>>,
11+
selections: Arc<Mutex<Vec<usize>>>,
12+
confirmations: Arc<Mutex<Vec<bool>>>,
13+
expect_error: Arc<Mutex<bool>>,
14+
}
15+
16+
impl TestUI {
17+
/// Create a new TestUI with no pre-programmed responses
18+
pub fn new() -> Self {
19+
Self {
20+
inputs: Arc::new(Mutex::new(Vec::new())),
21+
selections: Arc::new(Mutex::new(Vec::new())),
22+
confirmations: Arc::new(Mutex::new(Vec::new())),
23+
expect_error: Arc::new(Mutex::new(false)),
24+
}
25+
}
26+
27+
/// Add an input response
28+
pub fn with_input(self, input: &str) -> Self {
29+
self.inputs.lock().unwrap().push(input.to_string());
30+
self
31+
}
32+
33+
/// Add a selection response
34+
pub fn with_selection(self, selection: usize) -> Self {
35+
self.selections.lock().unwrap().push(selection);
36+
self
37+
}
38+
39+
/// Add a confirmation response
40+
pub fn with_confirmation(self, confirm: bool) -> Self {
41+
self.confirmations.lock().unwrap().push(confirm);
42+
self
43+
}
44+
45+
/// Indicate that the next operation should simulate an error/cancellation
46+
pub fn with_error(self) -> Self {
47+
*self.expect_error.lock().unwrap() = true;
48+
self
49+
}
50+
}
51+
52+
impl UserInterface for TestUI {
53+
fn multiselect(&self, _prompt: &str, items: &[String]) -> Result<Vec<usize>> {
54+
// For tests, we don't support multiselect - just return empty selection
55+
let _ = items;
56+
Ok(vec![])
57+
}
58+
fn input(&self, _prompt: &str) -> Result<String> {
59+
let mut inputs = self.inputs.lock().unwrap();
60+
if *self.expect_error.lock().unwrap() {
61+
*self.expect_error.lock().unwrap() = false;
62+
return Err(anyhow::anyhow!("User cancelled input"));
63+
}
64+
if inputs.is_empty() {
65+
return Err(anyhow::anyhow!("No more test inputs"));
66+
}
67+
Ok(inputs.remove(0))
68+
}
69+
70+
fn input_with_default(&self, _prompt: &str, default: &str) -> Result<String> {
71+
let mut inputs = self.inputs.lock().unwrap();
72+
if *self.expect_error.lock().unwrap() {
73+
*self.expect_error.lock().unwrap() = false;
74+
return Err(anyhow::anyhow!("User cancelled input"));
75+
}
76+
if inputs.is_empty() {
77+
Ok(default.to_string())
78+
} else {
79+
Ok(inputs.remove(0))
80+
}
81+
}
82+
83+
fn select(&self, _prompt: &str, _items: &[String]) -> Result<usize> {
84+
let mut selections = self.selections.lock().unwrap();
85+
if *self.expect_error.lock().unwrap() {
86+
*self.expect_error.lock().unwrap() = false;
87+
return Err(anyhow::anyhow!("User cancelled selection"));
88+
}
89+
if selections.is_empty() {
90+
return Err(anyhow::anyhow!("No more test selections"));
91+
}
92+
Ok(selections.remove(0))
93+
}
94+
95+
fn select_with_default(
96+
&self,
97+
_prompt: &str,
98+
_items: &[String],
99+
default: usize,
100+
) -> Result<usize> {
101+
let mut selections = self.selections.lock().unwrap();
102+
if *self.expect_error.lock().unwrap() {
103+
*self.expect_error.lock().unwrap() = false;
104+
return Err(anyhow::anyhow!("User cancelled selection"));
105+
}
106+
if selections.is_empty() {
107+
Ok(default)
108+
} else {
109+
Ok(selections.remove(0))
110+
}
111+
}
112+
113+
fn fuzzy_select(&self, _prompt: &str, items: &[String]) -> Result<usize> {
114+
// For tests, fuzzy select behaves like regular select
115+
self.select(_prompt, items)
116+
}
117+
118+
fn confirm(&self, _prompt: &str) -> Result<bool> {
119+
let mut confirmations = self.confirmations.lock().unwrap();
120+
if *self.expect_error.lock().unwrap() {
121+
*self.expect_error.lock().unwrap() = false;
122+
return Err(anyhow::anyhow!("User cancelled confirmation"));
123+
}
124+
if confirmations.is_empty() {
125+
return Err(anyhow::anyhow!("No more test confirmations"));
126+
}
127+
Ok(confirmations.remove(0))
128+
}
129+
130+
fn confirm_with_default(&self, _prompt: &str, default: bool) -> Result<bool> {
131+
let mut confirmations = self.confirmations.lock().unwrap();
132+
if *self.expect_error.lock().unwrap() {
133+
*self.expect_error.lock().unwrap() = false;
134+
return Err(anyhow::anyhow!("User cancelled confirmation"));
135+
}
136+
if confirmations.is_empty() {
137+
Ok(default)
138+
} else {
139+
Ok(confirmations.remove(0))
140+
}
141+
}
142+
}
143+
144+
impl Default for TestUI {
145+
fn default() -> Self {
146+
Self::new()
147+
}
148+
}

0 commit comments

Comments
 (0)