Skip to content

Commit eaa8625

Browse files
committed
tests: add tests for resolving symlinks and extracting Poetry version from Homebrew paths
1 parent 82590d6 commit eaa8625

File tree

2 files changed

+190
-5
lines changed

2 files changed

+190
-5
lines changed

crates/pet-fs/src/path.rs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -618,4 +618,80 @@ mod tests {
618618
result
619619
);
620620
}
621+
622+
// ==================== resolve_any_symlink tests ====================
623+
624+
#[test]
625+
fn test_resolve_any_symlink_nonexistent_path() {
626+
// Non-existent paths should return None
627+
let nonexistent = PathBuf::from("/this/path/does/not/exist/anywhere");
628+
assert_eq!(resolve_any_symlink(&nonexistent), None);
629+
}
630+
631+
#[test]
632+
fn test_resolve_any_symlink_regular_file() {
633+
// Regular files (not symlinks) should return None
634+
use std::io::Write;
635+
let temp_dir = std::env::temp_dir();
636+
let test_file = temp_dir.join("pet_test_regular_file.txt");
637+
638+
// Create a regular file
639+
let mut file = std::fs::File::create(&test_file).expect("Failed to create test file");
640+
file.write_all(b"test").expect("Failed to write test file");
641+
642+
// resolve_any_symlink should return None for regular files
643+
assert_eq!(resolve_any_symlink(&test_file), None);
644+
645+
// Clean up
646+
let _ = std::fs::remove_file(&test_file);
647+
}
648+
649+
#[test]
650+
fn test_resolve_any_symlink_directory() {
651+
// Directories (not symlinks) should return None
652+
let temp_dir = std::env::temp_dir();
653+
let test_dir = temp_dir.join("pet_test_regular_dir");
654+
655+
// Create a regular directory
656+
let _ = std::fs::create_dir(&test_dir);
657+
658+
// resolve_any_symlink should return None for regular directories
659+
assert_eq!(resolve_any_symlink(&test_dir), None);
660+
661+
// Clean up
662+
let _ = std::fs::remove_dir(&test_dir);
663+
}
664+
665+
#[test]
666+
#[cfg(unix)]
667+
fn test_resolve_any_symlink_unix_symlink() {
668+
use std::os::unix::fs::symlink;
669+
670+
let temp_dir = std::env::temp_dir();
671+
let target_file = temp_dir.join("pet_test_symlink_target.txt");
672+
let symlink_path = temp_dir.join("pet_test_symlink.txt");
673+
674+
// Clean up any existing test files
675+
let _ = std::fs::remove_file(&target_file);
676+
let _ = std::fs::remove_file(&symlink_path);
677+
678+
// Create target file
679+
std::fs::write(&target_file, "test").expect("Failed to create target file");
680+
681+
// Create symlink
682+
symlink(&target_file, &symlink_path).expect("Failed to create symlink");
683+
684+
// resolve_any_symlink should return the target path
685+
let result = resolve_any_symlink(&symlink_path);
686+
assert!(result.is_some(), "Should resolve symlink");
687+
688+
let resolved = result.unwrap();
689+
// The resolved path should be canonicalized, so compare canonical forms
690+
let expected = std::fs::canonicalize(&target_file).unwrap();
691+
assert_eq!(resolved, expected);
692+
693+
// Clean up
694+
let _ = std::fs::remove_file(&symlink_path);
695+
let _ = std::fs::remove_file(&target_file);
696+
}
621697
}

crates/pet-poetry/src/manager.rs

Lines changed: 114 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,10 @@ impl PoetryManager {
2727
if let Some(executable) = executable {
2828
if executable.is_file() {
2929
let version = Self::extract_version_from_path(&executable);
30-
return Some(PoetryManager { executable, version });
30+
return Some(PoetryManager {
31+
executable,
32+
version,
33+
});
3134
}
3235
}
3336

@@ -119,7 +122,10 @@ impl PoetryManager {
119122
for executable in search_paths {
120123
if executable.is_file() {
121124
let version = Self::extract_version_from_path(&executable);
122-
return Some(PoetryManager { executable, version });
125+
return Some(PoetryManager {
126+
executable,
127+
version,
128+
});
123129
}
124130
}
125131

@@ -129,13 +135,19 @@ impl PoetryManager {
129135
let executable = each.join("poetry");
130136
if executable.is_file() {
131137
let version = Self::extract_version_from_path(&executable);
132-
return Some(PoetryManager { executable, version });
138+
return Some(PoetryManager {
139+
executable,
140+
version,
141+
});
133142
}
134143
if std::env::consts::OS == "windows" {
135144
let executable = each.join("poetry.exe");
136145
if executable.is_file() {
137146
let version = Self::extract_version_from_path(&executable);
138-
return Some(PoetryManager { executable, version });
147+
return Some(PoetryManager {
148+
executable,
149+
version,
150+
});
139151
}
140152
}
141153
}
@@ -162,7 +174,11 @@ impl PoetryManager {
162174
if let Some(captures) = HOMEBREW_POETRY_VERSION.captures(&path_str) {
163175
if let Some(version_match) = captures.get(1) {
164176
let version = version_match.as_str().to_string();
165-
trace!("Extracted Poetry version {} from Homebrew path: {:?}", version, resolved);
177+
trace!(
178+
"Extracted Poetry version {} from Homebrew path: {:?}",
179+
version,
180+
resolved
181+
);
166182
return Some(version);
167183
}
168184
}
@@ -176,4 +192,97 @@ impl PoetryManager {
176192
tool: EnvManagerType::Poetry,
177193
}
178194
}
195+
196+
/// Extracts version from a path string using the Homebrew Cellar regex.
197+
/// This is exposed for testing purposes.
198+
#[cfg(test)]
199+
fn extract_version_from_path_str(path_str: &str) -> Option<String> {
200+
if let Some(captures) = HOMEBREW_POETRY_VERSION.captures(path_str) {
201+
captures.get(1).map(|m| m.as_str().to_string())
202+
} else {
203+
None
204+
}
205+
}
206+
}
207+
208+
#[cfg(test)]
209+
mod tests {
210+
use super::*;
211+
212+
#[test]
213+
fn test_extract_version_macos_arm() {
214+
// macOS ARM Homebrew path
215+
let path = "/opt/homebrew/Cellar/poetry/1.8.3/bin/poetry";
216+
assert_eq!(
217+
PoetryManager::extract_version_from_path_str(path),
218+
Some("1.8.3".to_string())
219+
);
220+
}
221+
222+
#[test]
223+
fn test_extract_version_macos_arm_with_revision() {
224+
// macOS ARM Homebrew path with revision suffix
225+
let path = "/opt/homebrew/Cellar/poetry/1.8.3_2/bin/poetry";
226+
assert_eq!(
227+
PoetryManager::extract_version_from_path_str(path),
228+
Some("1.8.3".to_string())
229+
);
230+
}
231+
232+
#[test]
233+
fn test_extract_version_macos_intel() {
234+
// macOS Intel Homebrew path
235+
let path = "/usr/local/Cellar/poetry/2.0.1/bin/poetry";
236+
assert_eq!(
237+
PoetryManager::extract_version_from_path_str(path),
238+
Some("2.0.1".to_string())
239+
);
240+
}
241+
242+
#[test]
243+
fn test_extract_version_linux() {
244+
// Linux Homebrew path
245+
let path = "/home/linuxbrew/.linuxbrew/Cellar/poetry/1.7.0/bin/poetry";
246+
assert_eq!(
247+
PoetryManager::extract_version_from_path_str(path),
248+
Some("1.7.0".to_string())
249+
);
250+
}
251+
252+
#[test]
253+
fn test_extract_version_non_homebrew_path() {
254+
// Non-Homebrew installation paths should return None
255+
let paths = [
256+
"/usr/local/bin/poetry",
257+
"/home/user/.local/bin/poetry",
258+
"/home/user/.poetry/bin/poetry",
259+
"C:\\Users\\user\\AppData\\Roaming\\pypoetry\\venv\\Scripts\\poetry.exe",
260+
];
261+
for path in paths {
262+
assert_eq!(
263+
PoetryManager::extract_version_from_path_str(path),
264+
None,
265+
"Expected None for path: {}",
266+
path
267+
);
268+
}
269+
}
270+
271+
#[test]
272+
fn test_extract_version_invalid_version_format() {
273+
// Invalid version formats should not match
274+
let paths = [
275+
"/opt/homebrew/Cellar/poetry/invalid/bin/poetry",
276+
"/opt/homebrew/Cellar/poetry/1.8/bin/poetry", // Missing patch version
277+
"/opt/homebrew/Cellar/poetry/v1.8.3/bin/poetry", // Has 'v' prefix
278+
];
279+
for path in paths {
280+
assert_eq!(
281+
PoetryManager::extract_version_from_path_str(path),
282+
None,
283+
"Expected None for path: {}",
284+
path
285+
);
286+
}
287+
}
179288
}

0 commit comments

Comments
 (0)