Skip to content

Commit 013bcfa

Browse files
committed
add tests
1 parent f2a59bf commit 013bcfa

File tree

2 files changed

+255
-0
lines changed

2 files changed

+255
-0
lines changed

crates/pet-python-utils/src/executable.rs

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,4 +381,137 @@ mod tests {
381381
PathBuf::from("/home/user/project/shims").as_path()
382382
));
383383
}
384+
385+
#[test]
386+
fn test_is_broken_symlink_regular_file() {
387+
// A regular file should not be detected as a broken symlink
388+
let temp_dir = std::env::temp_dir();
389+
let test_file = temp_dir.join("pet_test_regular_file.txt");
390+
fs::write(&test_file, "test").unwrap();
391+
392+
assert!(!is_broken_symlink(&test_file));
393+
394+
let _ = fs::remove_file(&test_file);
395+
}
396+
397+
#[test]
398+
fn test_is_broken_symlink_nonexistent() {
399+
// A non-existent path should not be detected as a broken symlink
400+
let nonexistent = PathBuf::from("/this/path/does/not/exist/python");
401+
assert!(!is_broken_symlink(&nonexistent));
402+
}
403+
404+
#[test]
405+
#[cfg(unix)]
406+
fn test_is_broken_symlink_unix() {
407+
use std::os::unix::fs::symlink;
408+
409+
let temp_dir = std::env::temp_dir();
410+
let target = temp_dir.join("pet_test_symlink_target_nonexistent");
411+
let link = temp_dir.join("pet_test_broken_symlink");
412+
413+
// Clean up any previous test artifacts
414+
let _ = fs::remove_file(&link);
415+
let _ = fs::remove_file(&target);
416+
417+
// Create a symlink to a non-existent target
418+
symlink(&target, &link).unwrap();
419+
420+
// The symlink should be detected as broken
421+
assert!(is_broken_symlink(&link));
422+
423+
// Clean up
424+
let _ = fs::remove_file(&link);
425+
}
426+
427+
#[test]
428+
#[cfg(unix)]
429+
fn test_is_broken_symlink_valid_symlink() {
430+
use std::os::unix::fs::symlink;
431+
432+
let temp_dir = std::env::temp_dir();
433+
let target = temp_dir.join("pet_test_symlink_target_exists");
434+
let link = temp_dir.join("pet_test_valid_symlink");
435+
436+
// Clean up any previous test artifacts
437+
let _ = fs::remove_file(&link);
438+
let _ = fs::remove_file(&target);
439+
440+
// Create the target file
441+
fs::write(&target, "test").unwrap();
442+
443+
// Create a symlink to the existing target
444+
symlink(&target, &link).unwrap();
445+
446+
// The symlink should NOT be detected as broken
447+
assert!(!is_broken_symlink(&link));
448+
449+
// Clean up
450+
let _ = fs::remove_file(&link);
451+
let _ = fs::remove_file(&target);
452+
}
453+
454+
#[test]
455+
fn test_find_executable_or_broken_not_found() {
456+
let temp_dir = std::env::temp_dir().join("pet_test_empty_env");
457+
let _ = fs::create_dir_all(&temp_dir);
458+
459+
match find_executable_or_broken(&temp_dir) {
460+
ExecutableResult::NotFound => (),
461+
other => panic!("Expected NotFound, got {:?}", other),
462+
}
463+
464+
let _ = fs::remove_dir_all(&temp_dir);
465+
}
466+
467+
#[test]
468+
fn test_find_executable_or_broken_found() {
469+
let temp_dir = std::env::temp_dir().join("pet_test_valid_env");
470+
#[cfg(windows)]
471+
let bin_dir = temp_dir.join("Scripts");
472+
#[cfg(unix)]
473+
let bin_dir = temp_dir.join("bin");
474+
475+
let _ = fs::remove_dir_all(&temp_dir);
476+
fs::create_dir_all(&bin_dir).unwrap();
477+
478+
#[cfg(windows)]
479+
let python_exe = bin_dir.join("python.exe");
480+
#[cfg(unix)]
481+
let python_exe = bin_dir.join("python");
482+
483+
fs::write(&python_exe, "fake python").unwrap();
484+
485+
match find_executable_or_broken(&temp_dir) {
486+
ExecutableResult::Found(path) => assert_eq!(path, python_exe),
487+
other => panic!("Expected Found, got {:?}", other),
488+
}
489+
490+
let _ = fs::remove_dir_all(&temp_dir);
491+
}
492+
493+
#[test]
494+
#[cfg(unix)]
495+
fn test_find_executable_or_broken_broken_symlink() {
496+
use std::os::unix::fs::symlink;
497+
498+
let temp_dir = std::env::temp_dir().join("pet_test_broken_env");
499+
let bin_dir = temp_dir.join("bin");
500+
501+
let _ = fs::remove_dir_all(&temp_dir);
502+
fs::create_dir_all(&bin_dir).unwrap();
503+
504+
let python_exe = bin_dir.join("python");
505+
let nonexistent_target = PathBuf::from("/nonexistent/python3.10");
506+
507+
// Create a broken symlink
508+
symlink(&nonexistent_target, &python_exe).unwrap();
509+
510+
match find_executable_or_broken(&temp_dir) {
511+
ExecutableResult::Broken(path) => assert_eq!(path, python_exe),
512+
other => panic!("Expected Broken, got {:?}", other),
513+
}
514+
515+
let _ = fs::remove_dir_all(&temp_dir);
516+
}
384517
}

crates/pet-venv/src/lib.rs

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,3 +138,125 @@ impl Locator for Venv {
138138
// We expect the user of this class to call `is_compatible`
139139
}
140140
}
141+
142+
#[cfg(test)]
143+
mod tests {
144+
use super::*;
145+
use std::fs;
146+
147+
#[test]
148+
fn test_try_environment_from_venv_dir_not_a_venv() {
149+
// A directory without pyvenv.cfg should return None
150+
let temp_dir = std::env::temp_dir().join("pet_test_not_a_venv");
151+
let _ = fs::remove_dir_all(&temp_dir);
152+
fs::create_dir_all(&temp_dir).unwrap();
153+
154+
let result = try_environment_from_venv_dir(&temp_dir);
155+
assert!(result.is_none());
156+
157+
let _ = fs::remove_dir_all(&temp_dir);
158+
}
159+
160+
#[test]
161+
fn test_try_environment_from_venv_dir_missing_executable() {
162+
// A venv with pyvenv.cfg but no Python executable
163+
let temp_dir = std::env::temp_dir().join("pet_test_venv_no_exe");
164+
let _ = fs::remove_dir_all(&temp_dir);
165+
fs::create_dir_all(&temp_dir).unwrap();
166+
167+
// Create pyvenv.cfg
168+
fs::write(
169+
temp_dir.join("pyvenv.cfg"),
170+
"version = 3.10.0\nprompt = test-env\n",
171+
)
172+
.unwrap();
173+
174+
let result = try_environment_from_venv_dir(&temp_dir);
175+
assert!(result.is_some());
176+
177+
let env = result.unwrap();
178+
assert_eq!(env.kind, Some(PythonEnvironmentKind::Venv));
179+
assert!(env.error.is_some());
180+
assert!(env.error.unwrap().contains("not found"));
181+
assert_eq!(env.name, Some("test-env".to_string()));
182+
183+
let _ = fs::remove_dir_all(&temp_dir);
184+
}
185+
186+
#[test]
187+
fn test_try_environment_from_venv_dir_valid() {
188+
// A valid venv with pyvenv.cfg and Python executable
189+
let temp_dir = std::env::temp_dir().join("pet_test_venv_valid");
190+
let _ = fs::remove_dir_all(&temp_dir);
191+
192+
#[cfg(windows)]
193+
let bin_dir = temp_dir.join("Scripts");
194+
#[cfg(unix)]
195+
let bin_dir = temp_dir.join("bin");
196+
197+
fs::create_dir_all(&bin_dir).unwrap();
198+
199+
// Create pyvenv.cfg
200+
fs::write(
201+
temp_dir.join("pyvenv.cfg"),
202+
"version = 3.11.0\nprompt = my-project\n",
203+
)
204+
.unwrap();
205+
206+
// Create python executable
207+
#[cfg(windows)]
208+
let python_exe = bin_dir.join("python.exe");
209+
#[cfg(unix)]
210+
let python_exe = bin_dir.join("python");
211+
212+
fs::write(&python_exe, "fake python").unwrap();
213+
214+
let result = try_environment_from_venv_dir(&temp_dir);
215+
assert!(result.is_some());
216+
217+
let env = result.unwrap();
218+
assert_eq!(env.kind, Some(PythonEnvironmentKind::Venv));
219+
assert!(env.error.is_none());
220+
assert!(env.executable.is_some());
221+
assert_eq!(env.name, Some("my-project".to_string()));
222+
223+
let _ = fs::remove_dir_all(&temp_dir);
224+
}
225+
226+
#[test]
227+
#[cfg(unix)]
228+
fn test_try_environment_from_venv_dir_broken_symlink() {
229+
use std::os::unix::fs::symlink;
230+
231+
// A venv with pyvenv.cfg but a broken symlink for Python
232+
let temp_dir = std::env::temp_dir().join("pet_test_venv_broken_symlink");
233+
let _ = fs::remove_dir_all(&temp_dir);
234+
235+
let bin_dir = temp_dir.join("bin");
236+
fs::create_dir_all(&bin_dir).unwrap();
237+
238+
// Create pyvenv.cfg
239+
fs::write(
240+
temp_dir.join("pyvenv.cfg"),
241+
"version = 3.9.0\nprompt = broken-env\n",
242+
)
243+
.unwrap();
244+
245+
// Create a broken symlink
246+
let python_exe = bin_dir.join("python");
247+
let nonexistent_target = std::path::PathBuf::from("/nonexistent/python3.9");
248+
symlink(&nonexistent_target, &python_exe).unwrap();
249+
250+
let result = try_environment_from_venv_dir(&temp_dir);
251+
assert!(result.is_some());
252+
253+
let env = result.unwrap();
254+
assert_eq!(env.kind, Some(PythonEnvironmentKind::Venv));
255+
assert!(env.error.is_some());
256+
assert!(env.error.as_ref().unwrap().contains("broken symlink"));
257+
assert_eq!(env.name, Some("broken-env".to_string()));
258+
assert!(env.executable.is_some());
259+
260+
let _ = fs::remove_dir_all(&temp_dir);
261+
}
262+
}

0 commit comments

Comments
 (0)