mirror of
https://github.com/openai/codex.git
synced 2026-10-31 19:28:49 +00:00
## Why Failed marketplace adds can leave temporary checkouts behind, and interrupted adds or upgrades can leave abandoned staging directories. ## What changed - Share a staging guard across marketplace adds and upgrades so temporary checkouts are removed on completion or failure, including config-write rollback. - Reclaim up to four abandoned directories per cleanup pass during add and upgrade operations. Use file locks to preserve active checkouts and operating-system-specific names to avoid reclaiming another platform's staging directories. - Skip symlinks and unrelated entries, and report cleanup failures as warnings. ## Testing Add regression coverage for failed adds, CLI and automatic-upgrade cleanup, cross-process ownership and abandonment, lock contention, bounded cleanup, and symlink handling. GitOrigin-RevId: 20999da0eab861dfb8025aad5e9fba2bcaf818b8
181 lines
5.5 KiB
Rust
181 lines
5.5 KiB
Rust
use super::replace_path_and_deduplicate;
|
|
use super::symlink_metadata_if_exists;
|
|
use pretty_assertions::assert_eq;
|
|
use std::path::Path;
|
|
|
|
#[test]
|
|
fn symlink_metadata_if_exists_distinguishes_files_directories_and_missing_paths()
|
|
-> std::io::Result<()> {
|
|
let directory = tempfile::tempdir()?;
|
|
let file = directory.path().join("file");
|
|
std::fs::write(&file, "contents")?;
|
|
|
|
let directory_metadata =
|
|
symlink_metadata_if_exists(directory.path())?.expect("directory exists");
|
|
let file_metadata = symlink_metadata_if_exists(&file)?.expect("file exists");
|
|
assert_eq!(
|
|
(directory_metadata.is_dir(), file_metadata.is_file()),
|
|
(true, true)
|
|
);
|
|
assert!(symlink_metadata_if_exists(&directory.path().join("missing"))?.is_none());
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn symlink_metadata_if_exists_preserves_io_errors() {
|
|
let error = symlink_metadata_if_exists(Path::new("invalid\0path"))
|
|
.expect_err("an invalid path must not be treated as missing");
|
|
assert_eq!(error.kind(), std::io::ErrorKind::InvalidInput);
|
|
}
|
|
|
|
#[test]
|
|
fn replace_path_and_deduplicate_preserves_other_paths_and_order() {
|
|
let old_path = Path::new("old");
|
|
let new_path = Path::new("new");
|
|
let extra = Path::new("extra");
|
|
let descendant = Path::new("old/child");
|
|
|
|
assert_eq!(
|
|
replace_path_and_deduplicate(
|
|
vec![old_path, extra, descendant, new_path, extra],
|
|
old_path,
|
|
new_path,
|
|
),
|
|
vec![new_path, extra, descendant]
|
|
);
|
|
}
|
|
|
|
#[cfg(unix)]
|
|
mod symlinks {
|
|
use super::super::resolve_symlink_write_paths;
|
|
use super::super::symlink_metadata_if_exists;
|
|
use pretty_assertions::assert_eq;
|
|
use std::os::unix::fs::symlink;
|
|
|
|
#[test]
|
|
fn symlink_metadata_if_exists_preserves_links_with_and_without_targets() -> std::io::Result<()>
|
|
{
|
|
let directory = tempfile::tempdir()?;
|
|
let target = directory.path().join("target");
|
|
let link = directory.path().join("link");
|
|
std::fs::write(&target, "contents")?;
|
|
symlink(&target, &link)?;
|
|
|
|
let linked_metadata = symlink_metadata_if_exists(&link)?.expect("symlink exists");
|
|
std::fs::remove_file(target)?;
|
|
let dangling_metadata = symlink_metadata_if_exists(&link)?.expect("symlink still exists");
|
|
assert!(linked_metadata.is_symlink());
|
|
assert_eq!(dangling_metadata.file_type(), linked_metadata.file_type());
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn symlink_cycles_fall_back_to_root_write_path() -> std::io::Result<()> {
|
|
let dir = tempfile::tempdir()?;
|
|
let a = dir.path().join("a");
|
|
let b = dir.path().join("b");
|
|
|
|
symlink(&b, &a)?;
|
|
symlink(&a, &b)?;
|
|
|
|
let resolved = resolve_symlink_write_paths(&a)?;
|
|
|
|
assert_eq!(resolved.read_path, None);
|
|
assert_eq!(resolved.write_path, a);
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[cfg(target_os = "linux")]
|
|
mod wsl {
|
|
use super::super::normalize_for_wsl_with_flag;
|
|
use pretty_assertions::assert_eq;
|
|
use std::path::PathBuf;
|
|
|
|
#[test]
|
|
fn wsl_mnt_drive_paths_lowercase() {
|
|
let normalized =
|
|
normalize_for_wsl_with_flag(PathBuf::from("/mnt/C/Users/Dev"), /*is_wsl*/ true);
|
|
|
|
assert_eq!(normalized, PathBuf::from("/mnt/c/users/dev"));
|
|
}
|
|
|
|
#[test]
|
|
fn wsl_non_drive_paths_unchanged() {
|
|
let path = PathBuf::from("/mnt/cc/Users/Dev");
|
|
let normalized = normalize_for_wsl_with_flag(path.clone(), /*is_wsl*/ true);
|
|
|
|
assert_eq!(normalized, path);
|
|
}
|
|
|
|
#[test]
|
|
fn wsl_non_mnt_paths_unchanged() {
|
|
let path = PathBuf::from("/home/Dev");
|
|
let normalized = normalize_for_wsl_with_flag(path.clone(), /*is_wsl*/ true);
|
|
|
|
assert_eq!(normalized, path);
|
|
}
|
|
}
|
|
|
|
mod native_workdir {
|
|
use super::super::normalize_for_native_workdir_with_flag;
|
|
use pretty_assertions::assert_eq;
|
|
use std::path::PathBuf;
|
|
|
|
#[cfg(target_os = "windows")]
|
|
#[test]
|
|
fn windows_verbatim_paths_are_simplified() {
|
|
let path = PathBuf::from(r"\\?\D:\c\x\worktrees\2508\swift-base");
|
|
let normalized = normalize_for_native_workdir_with_flag(path, /*is_windows*/ true);
|
|
|
|
assert_eq!(
|
|
normalized,
|
|
PathBuf::from(r"D:\c\x\worktrees\2508\swift-base")
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn non_windows_paths_are_unchanged() {
|
|
let path = PathBuf::from(r"\\?\D:\c\x\worktrees\2508\swift-base");
|
|
let normalized =
|
|
normalize_for_native_workdir_with_flag(path.clone(), /*is_windows*/ false);
|
|
|
|
assert_eq!(normalized, path);
|
|
}
|
|
}
|
|
|
|
mod path_comparison {
|
|
use super::super::paths_match_after_normalization;
|
|
use std::path::PathBuf;
|
|
|
|
#[test]
|
|
fn matches_identical_existing_paths() -> std::io::Result<()> {
|
|
let dir = tempfile::tempdir()?;
|
|
|
|
assert!(paths_match_after_normalization(dir.path(), dir.path()));
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn falls_back_to_raw_equality_when_paths_cannot_be_normalized() {
|
|
assert!(paths_match_after_normalization(
|
|
PathBuf::from("missing"),
|
|
PathBuf::from("missing"),
|
|
));
|
|
assert!(!paths_match_after_normalization(
|
|
PathBuf::from("missing-a"),
|
|
PathBuf::from("missing-b"),
|
|
));
|
|
}
|
|
|
|
#[cfg(windows)]
|
|
#[test]
|
|
fn matches_windows_verbatim_paths() -> std::io::Result<()> {
|
|
let dir = tempfile::tempdir()?;
|
|
let verbatim_dir = PathBuf::from(format!(r"\\?\{}", dir.path().display()));
|
|
|
|
assert!(paths_match_after_normalization(verbatim_dir, dir.path()));
|
|
Ok(())
|
|
}
|
|
}
|