From 089f1a723317ab48653df2607d80900142143f66 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 30 Jan 2026 06:49:37 +0000 Subject: [PATCH 1/4] Initial plan From f87aa41f3c75c87b049fc397ab12fddc6a7c72c0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 30 Jan 2026 06:53:23 +0000 Subject: [PATCH 2/4] Add validation for empty FOC_DEVNET_BASEDIR environment variable Co-authored-by: redpanda-f <181817029+redpanda-f@users.noreply.github.com> --- src/paths.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/paths.rs b/src/paths.rs index 199f717..8085e7c 100644 --- a/src/paths.rs +++ b/src/paths.rs @@ -2,11 +2,17 @@ use std::path::PathBuf; /// Returns the path to the foc-devnet home directory. /// First checks for $FOC_DEVNET_BASEDIR environment variable. -/// If not set, defaults to ~/.foc-devnet +/// If not set or empty, defaults to ~/.foc-devnet /// Supports tilde expansion for paths like ~/my-foc-devnet pub fn foc_devnet_home() -> PathBuf { if let Ok(base_dir) = std::env::var("FOC_DEVNET_BASEDIR") { - PathBuf::from(shellexpand::tilde(&base_dir).as_ref()) + if !base_dir.trim().is_empty() { + PathBuf::from(shellexpand::tilde(&base_dir).as_ref()) + } else { + dirs::home_dir() + .unwrap_or_else(|| PathBuf::from("/tmp")) + .join(".foc-devnet") + } } else { dirs::home_dir() .unwrap_or_else(|| PathBuf::from("/tmp")) From cb88141bb9208b83f3a022667836e39e276864ae Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 30 Jan 2026 06:55:43 +0000 Subject: [PATCH 3/4] Refactor to reduce code duplication and add comprehensive unit tests Co-authored-by: redpanda-f <181817029+redpanda-f@users.noreply.github.com> --- src/paths.rs | 65 +++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 59 insertions(+), 6 deletions(-) diff --git a/src/paths.rs b/src/paths.rs index 8085e7c..e401931 100644 --- a/src/paths.rs +++ b/src/paths.rs @@ -5,18 +5,20 @@ use std::path::PathBuf; /// If not set or empty, defaults to ~/.foc-devnet /// Supports tilde expansion for paths like ~/my-foc-devnet pub fn foc_devnet_home() -> PathBuf { + let default_path = || { + dirs::home_dir() + .unwrap_or_else(|| PathBuf::from("/tmp")) + .join(".foc-devnet") + }; + if let Ok(base_dir) = std::env::var("FOC_DEVNET_BASEDIR") { if !base_dir.trim().is_empty() { PathBuf::from(shellexpand::tilde(&base_dir).as_ref()) } else { - dirs::home_dir() - .unwrap_or_else(|| PathBuf::from("/tmp")) - .join(".foc-devnet") + default_path() } } else { - dirs::home_dir() - .unwrap_or_else(|| PathBuf::from("/tmp")) - .join(".foc-devnet") + default_path() } } @@ -257,3 +259,54 @@ pub fn project_root() -> Result { // Constants for container paths /// Container path where Filecoin proof parameters are mounted pub const CONTAINER_FILECOIN_PROOF_PARAMS_PATH: &str = "/var/tmp/filecoin-proof-parameters"; + +#[cfg(test)] +mod tests { + use super::*; + use std::env; + + #[test] + fn test_foc_devnet_home_with_valid_path() { + env::set_var("FOC_DEVNET_BASEDIR", "/custom/path"); + let path = foc_devnet_home(); + assert_eq!(path, PathBuf::from("/custom/path")); + env::remove_var("FOC_DEVNET_BASEDIR"); + } + + #[test] + fn test_foc_devnet_home_with_empty_string() { + env::set_var("FOC_DEVNET_BASEDIR", ""); + let path = foc_devnet_home(); + // Should fall back to default, which includes ".foc-devnet" + assert!(path.to_string_lossy().contains(".foc-devnet")); + env::remove_var("FOC_DEVNET_BASEDIR"); + } + + #[test] + fn test_foc_devnet_home_with_whitespace() { + env::set_var("FOC_DEVNET_BASEDIR", " "); + let path = foc_devnet_home(); + // Should fall back to default, which includes ".foc-devnet" + assert!(path.to_string_lossy().contains(".foc-devnet")); + env::remove_var("FOC_DEVNET_BASEDIR"); + } + + #[test] + fn test_foc_devnet_home_unset() { + env::remove_var("FOC_DEVNET_BASEDIR"); + let path = foc_devnet_home(); + // Should use default, which includes ".foc-devnet" + assert!(path.to_string_lossy().contains(".foc-devnet")); + } + + #[test] + fn test_foc_devnet_home_with_tilde() { + env::set_var("FOC_DEVNET_BASEDIR", "~/my-custom-foc"); + let path = foc_devnet_home(); + // Tilde should be expanded, so shouldn't start with ~ + assert!(!path.to_string_lossy().starts_with("~")); + // Should contain the custom directory name + assert!(path.to_string_lossy().contains("my-custom-foc")); + env::remove_var("FOC_DEVNET_BASEDIR"); + } +} From 42ef7291bbc66ed5e1feacf2de516b975c79445d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 30 Jan 2026 06:57:37 +0000 Subject: [PATCH 4/4] Add serial_test to prevent test race conditions Co-authored-by: redpanda-f <181817029+redpanda-f@users.noreply.github.com> --- Cargo.lock | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 5 +++- src/paths.rs | 6 ++++ 3 files changed, 90 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index f195c51..04e8fec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -864,6 +864,7 @@ dependencies = [ "reqwest 0.11.27", "serde", "serde_json", + "serial_test", "sha2 0.10.9", "sha3", "shellexpand", @@ -1564,6 +1565,15 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + [[package]] name = "log" version = "0.4.28" @@ -1824,6 +1834,29 @@ dependencies = [ "syn 2.0.110", ] +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + [[package]] name = "pbkdf2" version = "0.12.2" @@ -2287,6 +2320,15 @@ dependencies = [ "syn 2.0.110", ] +[[package]] +name = "scc" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46e6f046b7fef48e2660c57ed794263155d713de679057f2d0c169bfc6e756cc" +dependencies = [ + "sdd", +] + [[package]] name = "schannel" version = "0.1.28" @@ -2296,6 +2338,18 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sdd" +version = "3.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490dcfcbfef26be6800d11870ff2df8774fa6e86d047e3e8c8a76b25655e41ca" + [[package]] name = "sec1" version = "0.7.3" @@ -2415,6 +2469,32 @@ dependencies = [ "serde", ] +[[package]] +name = "serial_test" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d0b343e184fc3b7bb44dff0705fffcf4b3756ba6aff420dddd8b24ca145e555" +dependencies = [ + "futures-executor", + "futures-util", + "log", + "once_cell", + "parking_lot", + "scc", + "serial_test_derive", +] + +[[package]] +name = "serial_test_derive" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f50427f258fb77356e4cd4aa0e87e2bd2c66dbcee41dc405282cae2bfc26c83" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.110", +] + [[package]] name = "sha2" version = "0.9.9" diff --git a/Cargo.toml b/Cargo.toml index 3b93d57..c1a3477 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,4 +37,7 @@ reqwest = { version = "0.11", features = ["blocking"] } tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] } sha2 = "0.10" -num_cpus = "1.16" \ No newline at end of file +num_cpus = "1.16" + +[dev-dependencies] +serial_test = "3.0" \ No newline at end of file diff --git a/src/paths.rs b/src/paths.rs index e401931..47c21bc 100644 --- a/src/paths.rs +++ b/src/paths.rs @@ -263,9 +263,11 @@ pub const CONTAINER_FILECOIN_PROOF_PARAMS_PATH: &str = "/var/tmp/filecoin-proof- #[cfg(test)] mod tests { use super::*; + use serial_test::serial; use std::env; #[test] + #[serial] fn test_foc_devnet_home_with_valid_path() { env::set_var("FOC_DEVNET_BASEDIR", "/custom/path"); let path = foc_devnet_home(); @@ -274,6 +276,7 @@ mod tests { } #[test] + #[serial] fn test_foc_devnet_home_with_empty_string() { env::set_var("FOC_DEVNET_BASEDIR", ""); let path = foc_devnet_home(); @@ -283,6 +286,7 @@ mod tests { } #[test] + #[serial] fn test_foc_devnet_home_with_whitespace() { env::set_var("FOC_DEVNET_BASEDIR", " "); let path = foc_devnet_home(); @@ -292,6 +296,7 @@ mod tests { } #[test] + #[serial] fn test_foc_devnet_home_unset() { env::remove_var("FOC_DEVNET_BASEDIR"); let path = foc_devnet_home(); @@ -300,6 +305,7 @@ mod tests { } #[test] + #[serial] fn test_foc_devnet_home_with_tilde() { env::set_var("FOC_DEVNET_BASEDIR", "~/my-custom-foc"); let path = foc_devnet_home();