diff --git a/radicle-node/src/test/environment.rs b/radicle-node/src/test/environment.rs index 48037e91..cca8f149 100644 --- a/radicle-node/src/test/environment.rs +++ b/radicle-node/src/test/environment.rs @@ -71,9 +71,7 @@ impl Environment { /// Create a new profile in this environment. /// This should be used when a running node is not required. pub fn profile(&mut self, name: &str) -> Profile { - let home = Home::new(self.tmp().join("home").join(name)) - .init() - .unwrap(); + let home = Home::new(self.tmp().join("home").join(name)).unwrap(); let storage = Storage::open(home.storage()).unwrap(); let keystore = Keystore::new(&home.keys()); let keypair = KeyPair::from_seed(Seed::from([!(self.users as u8); 32])); @@ -232,7 +230,7 @@ impl Node { .take(8) .collect::(), ); - let home = Home::new(home).init().unwrap(); + let home = Home::new(home).unwrap(); let signer = MockSigner::default(); let storage = Storage::open(home.storage()).unwrap(); diff --git a/radicle/src/profile.rs b/radicle/src/profile.rs index 8c1b4b2d..c5d8b4c3 100644 --- a/radicle/src/profile.rs +++ b/radicle/src/profile.rs @@ -67,7 +67,6 @@ pub struct Profile { impl Profile { pub fn init(home: Home, passphrase: impl Into) -> Result { - let home = home.init()?; let storage = Storage::open(home.storage())?; let keystore = Keystore::new(&home.keys()); let public_key = keystore.init("radicle", passphrase)?; @@ -142,9 +141,9 @@ impl Profile { /// Get the path to the radicle home folder. pub fn home() -> Result { if let Some(home) = env::var_os(env::RAD_HOME) { - Ok(Home::new(PathBuf::from(home))) + Ok(Home::new(PathBuf::from(home))?) } else if let Some(home) = env::var_os("HOME") { - Ok(Home::new(PathBuf::from(home).join(".radicle"))) + Ok(Home::new(PathBuf::from(home).join(".radicle"))?) } else { Err(io::Error::new( io::ErrorKind::NotFound, @@ -166,14 +165,33 @@ impl From for Home { } impl Home { - pub fn init(self) -> Result { - fs::create_dir_all(self.node()).ok(); + /// Creates the Radicle Home directories. + /// + /// The `home` path is used as the base directory for all + /// necessary subdirectories. + /// + /// If `home` does not already exist then it and any + /// subdirectories are created using [`fs::create_dir_all`]. + /// + /// The `home` path is also canonicalized using [`fs::canonicalize`]. + /// + /// All necessary subdirectories are also created. + pub fn new(home: impl Into) -> Result { + let path = home.into(); + if !path.exists() { + fs::create_dir_all(path.clone())?; + } + let home = Self { + path: path.canonicalize()?, + }; - Ok(self) - } + for dir in &[home.storage(), home.keys(), home.node()] { + if !dir.exists() { + fs::create_dir_all(dir)?; + } + } - pub fn new(home: impl Into) -> Self { - Self { path: home.into() } + Ok(home) } pub fn path(&self) -> &Path { @@ -198,3 +216,33 @@ impl Home { .unwrap_or_else(|| self.node().join(node::DEFAULT_SOCKET_NAME)) } } + +#[cfg(test)] +mod test { + use std::fs; + + use super::Home; + + // Checks that if we have: + // '/run/user/1000/.tmpqfK6ih/../.tmpqfK6ih/Radicle/Home' + // + // that it gets normalized to: + // '/run/user/1000/.tmpqfK6ih/Radicle/Home' + #[test] + fn canonicalize_home() { + let tmp = tempfile::tempdir().unwrap(); + let path = tmp.path().join("Home").join("Radicle"); + fs::create_dir_all(path.clone()).unwrap(); + + let last = tmp.path().components().last().unwrap(); + let home = Home::new( + tmp.path() + .join("..") + .join(last) + .join("Home") + .join("Radicle"), + ) + .unwrap(); + assert_eq!(home.path, path); + } +} diff --git a/radicle/src/test.rs b/radicle/src/test.rs index 47cc6762..5e96a722 100644 --- a/radicle/src/test.rs +++ b/radicle/src/test.rs @@ -19,7 +19,7 @@ pub mod setup { let mut rng = fastrand::Rng::new(); let signer = MockSigner::new(&mut rng); let home = tmp.path().join("home"); - let paths = Home::new(home.as_path()); + let paths = Home::new(home.as_path()).unwrap(); let storage = Storage::open(paths.storage()).unwrap(); let (id, _, _, _) = fixtures::project(tmp.path().join("copy"), &storage, &signer).unwrap(); let project = storage.repository(id).unwrap();