diff --git a/crates/pu-core/src/agent_def.rs b/crates/pu-core/src/agent_def.rs index 4a48014..614e751 100644 --- a/crates/pu-core/src/agent_def.rs +++ b/crates/pu-core/src/agent_def.rs @@ -144,7 +144,7 @@ mod tests { use tempfile::TempDir; fn isolate_home(tmp: &TempDir) { - unsafe { std::env::set_var("HOME", tmp.path()) }; + paths::set_home_override(Some(tmp.path().to_path_buf())); } #[test] diff --git a/crates/pu-core/src/paths.rs b/crates/pu-core/src/paths.rs index fcf7698..3f89cf1 100644 --- a/crates/pu-core/src/paths.rs +++ b/crates/pu-core/src/paths.rs @@ -72,7 +72,25 @@ pub fn global_schedules_dir() -> Result { Ok(global_pu_dir()?.join("schedules")) } +#[cfg(test)] +thread_local! { + static HOME_OVERRIDE: std::cell::RefCell> = const { std::cell::RefCell::new(None) }; +} + +/// Override `home_dir()` for the current test thread. Thread-safe alternative +/// to `unsafe { std::env::set_var("HOME", ...) }`. +#[cfg(test)] +pub(crate) fn set_home_override(path: Option) { + HOME_OVERRIDE.with(|o| *o.borrow_mut() = path); +} + fn home_dir() -> Result { + #[cfg(test)] + { + if let Some(p) = HOME_OVERRIDE.with(|o| o.borrow().clone()) { + return Ok(p); + } + } std::env::var("HOME") .map(PathBuf::from) .map_err(|_| std::io::Error::new(std::io::ErrorKind::NotFound, "$HOME not set")) diff --git a/crates/pu-core/src/schedule_def.rs b/crates/pu-core/src/schedule_def.rs index b937a0e..4fb49c7 100644 --- a/crates/pu-core/src/schedule_def.rs +++ b/crates/pu-core/src/schedule_def.rs @@ -328,6 +328,10 @@ mod tests { use super::*; use tempfile::TempDir; + fn isolate_home(tmp: &TempDir) { + paths::set_home_override(Some(tmp.path().to_path_buf())); + } + fn make_trigger() -> ScheduleTrigger { ScheduleTrigger::AgentDef { name: "security-review".to_string(), @@ -504,6 +508,7 @@ created_at: "2025-06-01T00:00:00Z" #[test] fn given_local_and_global_schedule_defs_should_list_local_first() { let tmp = TempDir::new().unwrap(); + isolate_home(&tmp); let root = tmp.path(); let local_dir = paths::schedules_dir(root); std::fs::create_dir_all(&local_dir).unwrap(); @@ -538,6 +543,7 @@ created_at: "2025-06-01T00:00:00Z" #[test] fn given_no_schedule_defs_should_return_empty_list() { let tmp = TempDir::new().unwrap(); + isolate_home(&tmp); let defs = list_schedule_defs(tmp.path()); assert!(defs.is_empty()); } diff --git a/crates/pu-core/src/swarm_def.rs b/crates/pu-core/src/swarm_def.rs index f766354..5312838 100644 --- a/crates/pu-core/src/swarm_def.rs +++ b/crates/pu-core/src/swarm_def.rs @@ -149,7 +149,7 @@ mod tests { use tempfile::TempDir; fn isolate_home(tmp: &TempDir) { - unsafe { std::env::set_var("HOME", tmp.path()) }; + paths::set_home_override(Some(tmp.path().to_path_buf())); } #[test] diff --git a/crates/pu-core/src/template.rs b/crates/pu-core/src/template.rs index 582de67..b6f7ce5 100644 --- a/crates/pu-core/src/template.rs +++ b/crates/pu-core/src/template.rs @@ -266,7 +266,7 @@ mod tests { /// Override HOME so global_pu_dir() points to an empty temp dir. fn isolate_home(tmp: &TempDir) { - unsafe { std::env::set_var("HOME", tmp.path()) }; + paths::set_home_override(Some(tmp.path().to_path_buf())); } #[test]