use crate::cmds::Existing;
use holochain_conductor_api::conductor::paths::{ConfigFilePath, ConfigRootPath};
use std::collections::HashSet;
use std::path::{Path, PathBuf};
use std::sync::OnceLock;
pub fn save(hc_dir: PathBuf, paths: Vec<ConfigRootPath>) -> std::io::Result<()> {
use std::io::Write;
std::fs::create_dir_all(&hc_dir).map_err(|err| {
std::io::Error::new(
err.kind(),
format!("Failed to create directory '{}': {}", hc_dir.display(), err),
)
})?;
let hc_file = hc_dir.join(".hc");
let mut file = std::fs::OpenOptions::new()
.append(true)
.create(true)
.open(hc_file)
.map_err(|err| {
std::io::Error::new(
err.kind(),
format!(
"Failed to create `.hc` file at '{}': {}",
hc_dir.display(),
err
),
)
})?;
for path in paths {
writeln!(file, "{}", path.display())?;
}
Ok(())
}
pub fn remove(hc_dir: PathBuf, existing: Existing) -> std::io::Result<usize> {
let sandboxes = load(hc_dir.clone())?;
let mut to_remove_indices: Vec<usize> = Vec::new();
if existing.all {
to_remove_indices = (0..sandboxes.len()).collect();
} else {
existing
.indices
.into_iter()
.for_each(|i| match sandboxes.get(i) {
None => msg!("Warning: Provided index is out of range: {}", i),
Some(Err(path)) => {
msg!(
"Warning: Missing or invalid sandbox {}:{}",
i,
path.display()
);
to_remove_indices.push(i);
}
Some(Ok(_)) => to_remove_indices.push(i),
});
}
let indices_to_remove: HashSet<_> = to_remove_indices.iter().collect();
let remaining: Vec<ConfigRootPath> = sandboxes
.iter()
.enumerate()
.filter_map(|(i, item)| {
if indices_to_remove.contains(&i) {
return None;
}
let Ok(item) = item else {
return None;
};
Some(ConfigRootPath::from(item.clone()))
})
.collect();
for i in to_remove_indices.iter() {
let p = match sandboxes.get(*i).unwrap() {
Ok(p) => p,
Err(p) => p,
};
if let Err(e) = std::fs::remove_dir_all(p) {
tracing::error!("Failed to remove {} because {:?}", p.display(), e);
msg!(
"Failed to remove sandbox {}:{}\nReason: {}",
i,
p.display(),
e
);
}
}
let hc_file = hc_dir.join(".hc");
if hc_file.exists() {
std::fs::remove_file(&hc_file).map_err(|err| {
std::io::Error::new(
err.kind(),
format!(
"Failed to remove '.hc' at {}\nReason: {}",
hc_dir.display(),
err
),
)
})?;
}
if !remaining.is_empty() {
save(hc_dir, remaining.clone())?;
} else {
for entry in std::fs::read_dir(&hc_dir)? {
let Ok(entry) = entry else { continue };
let Ok(file_type) = entry.file_type() else {
continue;
};
if file_type.is_file() {
if let Some(s) = entry.file_name().to_str() {
if s.starts_with(".hc_live_") {
std::fs::remove_file(entry.path()).map_err(|err| {
std::io::Error::new(
err.kind(),
format!(
"Failed to remove '{}' at {}\nReason: {}",
s,
hc_dir.display(),
err
),
)
})?
}
}
}
}
let hc_auth = hc_dir.join(".hc_auth");
if hc_auth.exists() {
std::fs::remove_file(&hc_auth).map_err(|err| {
std::io::Error::new(
err.kind(),
format!(
"Failed to remove '.hc_auth' at {}\nReason: {}",
hc_dir.display(),
err
),
)
})?;
}
}
Ok(sandboxes.len() - remaining.len())
}
pub fn load(hc_dir: PathBuf) -> std::io::Result<Vec<Result<PathBuf, PathBuf>>> {
let mut paths = Vec::new();
let hc_file = hc_dir.join(".hc");
if hc_file.exists() {
let existing = std::fs::read_to_string(hc_file).map_err(|err| {
std::io::Error::new(
err.kind(),
format!("Failed to read file at '{}': {}", hc_dir.display(), err),
)
})?;
for sandbox in existing.lines() {
let path = PathBuf::from(sandbox);
let config_file_path = ConfigFilePath::from(ConfigRootPath::from(path.clone()));
if config_file_path.as_ref().exists() {
paths.push(Ok(path));
} else {
tracing::error!("Failed to load path {} from existing .hc", path.display());
paths.push(Err(path));
}
}
}
Ok(paths)
}
pub fn list(hc_dir: PathBuf, verbose: bool) -> std::io::Result<()> {
let sandboxes = load(hc_dir.clone())?;
if sandboxes.is_empty() {
msg!("No sandboxes contained in {}", hc_dir.join(".hc").display());
return Ok(());
}
let out = sandboxes.into_iter().enumerate().try_fold(
"\nSandboxes contained in `.hc`\n".to_string(),
|out, (i, result)| {
let r = match (result, verbose) {
(Err(path), _) => format!("{}{}: Missing ({})\n", out, i, path.display()),
(Ok(path), false) => format!("{}{}: {}\n", out, i, path.display()),
(Ok(path), true) => {
let config = holochain_conductor_config::config::read_config(
ConfigRootPath::from(path.clone()),
)
.map_err(std::io::Error::other)?;
format!(
"{}{}: {}\nConductor Config:\n{:?}\n",
out,
i,
path.display(),
config
)
}
};
std::io::Result::Ok(r)
},
)?;
msg!("{}", out);
Ok(())
}
fn get_file_locks() -> &'static tokio::sync::Mutex<Vec<usize>> {
static FILE_LOCKS: OnceLock<tokio::sync::Mutex<Vec<usize>>> = OnceLock::new();
FILE_LOCKS.get_or_init(|| tokio::sync::Mutex::new(Vec::new()))
}
pub async fn lock_live(mut hc_dir: PathBuf, path: &Path, port: u16) -> anyhow::Result<()> {
use std::io::Write;
std::fs::create_dir_all(&hc_dir)?;
let paths = load(hc_dir.clone())?;
let index = match paths
.into_iter()
.enumerate()
.find(|p| p.1 == Ok(path.to_path_buf()))
{
Some((i, _)) => i,
None => return Ok(()),
};
hc_dir.push(format!(".hc_live_{index}"));
match std::fs::OpenOptions::new()
.write(true)
.create_new(true)
.open(hc_dir)
{
Ok(mut file) => {
writeln!(file, "{port}")?;
let mut lock = get_file_locks().lock().await;
lock.push(index);
}
Err(e) => match e.kind() {
std::io::ErrorKind::AlreadyExists => {}
_ => return Err(e.into()),
},
}
Ok(())
}
pub fn load_ports(hc_dir: PathBuf) -> anyhow::Result<Vec<Option<u16>>> {
let mut ports = Vec::new();
let paths = load(hc_dir.clone())?;
for (i, _) in paths.into_iter().enumerate() {
let mut hc = hc_dir.clone();
hc.push(format!(".hc_live_{i}"));
if hc.exists() {
let live = std::fs::read_to_string(hc)?;
let p = live.lines().next().and_then(|l| l.parse::<u16>().ok());
ports.push(p)
} else {
ports.push(None);
}
}
Ok(ports)
}
pub fn find_ports(hc_dir: PathBuf, paths: &[PathBuf]) -> anyhow::Result<Vec<Option<u16>>> {
let mut ports = Vec::new();
let all_paths = load(hc_dir.clone())?;
for path in paths {
let index = all_paths.iter().position(|p| *p == Ok(path.to_path_buf()));
match index {
Some(i) => {
let mut hc = hc_dir.clone();
hc.push(format!(".hc_live_{i}"));
if hc.exists() {
let live = std::fs::read_to_string(hc)?;
let p = live.lines().next().and_then(|l| l.parse::<u16>().ok());
ports.push(p)
} else {
ports.push(None);
}
}
None => ports.push(None),
}
}
Ok(ports)
}
pub async fn release_ports(hc_dir: PathBuf) -> anyhow::Result<()> {
let files = get_file_locks().lock().await;
for file in files.iter() {
let mut hc = hc_dir.clone();
hc.push(format!(".hc_live_{file}"));
if hc.exists() {
std::fs::remove_file(hc)?;
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use holochain_conductor_api::conductor::paths::ConfigRootPath;
use std::fs;
#[test]
fn test_save_single_path() -> anyhow::Result<()> {
let temp_dir = tempfile::tempdir()?;
let test_dir = temp_dir.path();
let hc_dir = test_dir.join("hc_dir");
fs::create_dir_all(&hc_dir)?;
let sandbox_dir = test_dir.join("sandbox1");
fs::create_dir_all(&sandbox_dir)?;
let paths = vec![ConfigRootPath::from(sandbox_dir.clone())];
save(hc_dir.clone(), paths)?;
let hc_file = hc_dir.join(".hc");
assert!(hc_file.exists());
let content = fs::read_to_string(hc_file)?;
assert_eq!(content.trim(), sandbox_dir.to_string_lossy());
Ok(())
}
#[test]
fn test_save_multiple_paths() -> anyhow::Result<()> {
let temp_dir = tempfile::tempdir()?;
let test_dir = temp_dir.path();
let hc_dir = test_dir.join("hc_dir");
fs::create_dir_all(&hc_dir)?;
let sandbox1 = test_dir.join("sandbox1");
let sandbox2 = test_dir.join("sandbox2");
fs::create_dir_all(&sandbox1)?;
fs::create_dir_all(&sandbox2)?;
let paths = vec![
ConfigRootPath::from(sandbox1.clone()),
ConfigRootPath::from(sandbox2.clone()),
];
save(hc_dir.clone(), paths)?;
let hc_file = hc_dir.join(".hc");
assert!(hc_file.exists());
let content = fs::read_to_string(hc_file)?;
let lines: Vec<&str> = content.lines().collect();
assert_eq!(lines.len(), 2);
assert_eq!(lines[0], sandbox1.to_string_lossy());
assert_eq!(lines[1], sandbox2.to_string_lossy());
Ok(())
}
#[test]
fn test_save_to_nonexistent_directory() -> anyhow::Result<()> {
let temp_dir = tempfile::tempdir()?;
let test_dir = temp_dir.path();
let hc_dir = test_dir.join("nonexistent");
let sandbox_dir = test_dir.join("sandbox1");
fs::create_dir_all(&sandbox_dir)?;
let paths = vec![ConfigRootPath::from(sandbox_dir.clone())];
save(hc_dir.clone(), paths)?;
assert!(hc_dir.exists());
let hc_file = hc_dir.join(".hc");
assert!(hc_file.exists());
Ok(())
}
#[test]
fn test_save_append() -> anyhow::Result<()> {
let temp_dir = tempfile::tempdir()?;
let test_dir = temp_dir.path();
let hc_dir = test_dir.join("hc_dir");
fs::create_dir_all(&hc_dir)?;
let sandbox1 = test_dir.join("sandbox1");
let sandbox2 = test_dir.join("sandbox2");
fs::create_dir_all(&sandbox1)?;
fs::create_dir_all(&sandbox2)?;
let paths1 = vec![ConfigRootPath::from(sandbox1.clone())];
save(hc_dir.clone(), paths1)?;
let paths2 = vec![ConfigRootPath::from(sandbox2.clone())];
save(hc_dir.clone(), paths2)?;
let hc_file = hc_dir.join(".hc");
let content = fs::read_to_string(hc_file)?;
let lines: Vec<&str> = content.lines().collect();
assert_eq!(lines.len(), 2);
assert_eq!(lines[0], sandbox1.to_string_lossy());
assert_eq!(lines[1], sandbox2.to_string_lossy());
Ok(())
}
#[test]
fn test_load_empty_directory() -> anyhow::Result<()> {
let temp_dir = tempfile::tempdir()?;
let test_dir = temp_dir.path();
let hc_dir = test_dir.join("hc_dir");
fs::create_dir_all(&hc_dir)?;
let paths = load(hc_dir.clone())?;
assert!(paths.is_empty());
Ok(())
}
#[test]
fn test_load_valid_paths() -> anyhow::Result<()> {
let temp_dir = tempfile::tempdir()?;
let test_dir = temp_dir.path();
let hc_dir = test_dir.join("hc_dir");
fs::create_dir_all(&hc_dir)?;
let sandbox1 = test_dir.join("sandbox1");
let sandbox2 = test_dir.join("sandbox2");
fs::create_dir_all(&sandbox1)?;
fs::create_dir_all(&sandbox2)?;
let config_path1 = ConfigRootPath::from(sandbox1.clone());
let config_file_path1 = ConfigFilePath::from(config_path1.clone());
fs::create_dir_all(config_file_path1.as_ref().parent().unwrap())?;
fs::write(config_file_path1.as_ref(), "dummy config")?;
let config_path2 = ConfigRootPath::from(sandbox2.clone());
let config_file_path2 = ConfigFilePath::from(config_path2.clone());
fs::create_dir_all(config_file_path2.as_ref().parent().unwrap())?;
fs::write(config_file_path2.as_ref(), "dummy config")?;
let paths = vec![config_path1, config_path2];
save(hc_dir.clone(), paths)?;
let loaded_paths = load(hc_dir.clone())?;
assert_eq!(loaded_paths.len(), 2);
assert_eq!(loaded_paths[0], Ok(sandbox1.clone()));
assert_eq!(loaded_paths[1], Ok(sandbox2.clone()));
Ok(())
}
#[test]
fn test_load_after_directory_removal() -> anyhow::Result<()> {
let temp_dir = tempfile::tempdir()?;
let test_dir = temp_dir.path();
let hc_dir = test_dir.join("hc_dir");
fs::create_dir_all(&hc_dir)?;
let sandbox1 = test_dir.join("sandbox1");
let sandbox2 = test_dir.join("sandbox2");
fs::create_dir_all(&sandbox1)?;
fs::create_dir_all(&sandbox2)?;
let config_path1 = ConfigRootPath::from(sandbox1.clone());
let config_file_path1 = ConfigFilePath::from(config_path1.clone());
fs::create_dir_all(config_file_path1.as_ref().parent().unwrap())?;
fs::write(config_file_path1.as_ref(), "dummy config")?;
let config_path2 = ConfigRootPath::from(sandbox2.clone());
let config_file_path2 = ConfigFilePath::from(config_path2.clone());
fs::create_dir_all(config_file_path2.as_ref().parent().unwrap())?;
fs::write(config_file_path2.as_ref(), "dummy config")?;
let paths = vec![config_path1, config_path2];
save(hc_dir.clone(), paths)?;
fs::remove_dir_all(&sandbox2)?;
let loaded_paths = load(hc_dir.clone())?;
assert_eq!(loaded_paths.len(), 2);
assert_eq!(loaded_paths[0], Ok(sandbox1.clone()));
assert_eq!(loaded_paths[1], Err(sandbox2.clone()));
Ok(())
}
#[test]
fn test_load_after_file_removal() -> anyhow::Result<()> {
let temp_dir = tempfile::tempdir()?;
let test_dir = temp_dir.path();
let hc_dir = test_dir.join("hc_dir");
fs::create_dir_all(&hc_dir)?;
let sandbox1 = test_dir.join("sandbox1");
let sandbox2 = test_dir.join("sandbox2");
fs::create_dir_all(&sandbox1)?;
fs::create_dir_all(&sandbox2)?;
let config_path1 = ConfigRootPath::from(sandbox1.clone());
let config_file_path1 = ConfigFilePath::from(config_path1.clone());
fs::create_dir_all(config_file_path1.as_ref().parent().unwrap())?;
fs::write(config_file_path1.as_ref(), "dummy config")?;
let config_path2 = ConfigRootPath::from(sandbox2.clone());
let config_file_path2 = ConfigFilePath::from(config_path2.clone());
fs::create_dir_all(config_file_path2.as_ref().parent().unwrap())?;
fs::write(config_file_path2.as_ref(), "dummy config")?;
let paths = vec![config_path1, config_path2];
save(hc_dir.clone(), paths)?;
fs::remove_file(config_file_path2.as_ref())?;
let loaded_paths = load(hc_dir.clone())?;
assert_eq!(loaded_paths.len(), 2);
assert_eq!(loaded_paths[0], Ok(sandbox1.clone()));
assert_eq!(loaded_paths[1], Err(sandbox2.clone()));
Ok(())
}
#[test]
fn test_remove_specific_sandboxes() -> anyhow::Result<()> {
let temp_dir = tempfile::tempdir()?;
let test_dir = temp_dir.path();
let hc_dir = test_dir.join("hc_dir");
fs::create_dir_all(&hc_dir)?;
let sandbox1 = test_dir.join("sandbox1");
let sandbox2 = test_dir.join("sandbox2");
let sandbox3 = test_dir.join("sandbox3");
fs::create_dir_all(&sandbox1)?;
fs::create_dir_all(&sandbox2)?;
fs::create_dir_all(&sandbox3)?;
let config_path1 = ConfigRootPath::from(sandbox1.clone());
let config_file_path1 = ConfigFilePath::from(config_path1.clone());
fs::create_dir_all(config_file_path1.as_ref().parent().unwrap())?;
fs::write(config_file_path1.as_ref(), "dummy config")?;
let config_path2 = ConfigRootPath::from(sandbox2.clone());
let config_file_path2 = ConfigFilePath::from(config_path2.clone());
fs::create_dir_all(config_file_path2.as_ref().parent().unwrap())?;
fs::write(config_file_path2.as_ref(), "dummy config")?;
let config_path3 = ConfigRootPath::from(sandbox3.clone());
let config_file_path3 = ConfigFilePath::from(config_path3.clone());
fs::create_dir_all(config_file_path3.as_ref().parent().unwrap())?;
fs::write(config_file_path3.as_ref(), "dummy config")?;
let paths = vec![config_path1, config_path2, config_path3];
save(hc_dir.clone(), paths)?;
remove(
hc_dir.clone(),
Existing {
all: false,
indices: vec![1],
},
)?;
assert!(sandbox1.exists());
assert!(!sandbox2.exists());
assert!(sandbox3.exists());
let hc_file = hc_dir.join(".hc");
assert!(hc_file.exists());
let loaded_paths = load(hc_dir.clone())?;
assert_eq!(loaded_paths.len(), 2);
assert_eq!(loaded_paths[0], Ok(sandbox1.clone()));
assert_eq!(loaded_paths[1], Ok(sandbox3.clone()));
remove(
hc_dir.clone(),
Existing {
all: false,
indices: vec![0, 1],
},
)?;
assert!(!sandbox1.exists());
assert!(!sandbox3.exists());
let hc_file = hc_dir.join(".hc");
assert!(!hc_file.exists());
Ok(())
}
#[test]
fn test_remove_all_sandboxes() -> anyhow::Result<()> {
let temp_dir = tempfile::tempdir()?;
let test_dir = temp_dir.path();
let hc_dir = test_dir.join("hc_dir");
fs::create_dir_all(&hc_dir)?;
remove(
hc_dir.clone(),
Existing {
all: true,
indices: vec![],
},
)?;
let hc_file = hc_dir.join(".hc");
assert!(!hc_file.exists());
let sandbox1 = test_dir.join("sandbox1");
let sandbox2 = test_dir.join("sandbox2");
fs::create_dir_all(&sandbox1)?;
fs::create_dir_all(&sandbox2)?;
let config_path1 = ConfigRootPath::from(sandbox1.clone());
let config_file_path1 = ConfigFilePath::from(config_path1.clone());
fs::create_dir_all(config_file_path1.as_ref().parent().unwrap())?;
fs::write(config_file_path1.as_ref(), "dummy config")?;
let config_path2 = ConfigRootPath::from(sandbox2.clone());
let config_file_path2 = ConfigFilePath::from(config_path2.clone());
fs::create_dir_all(config_file_path2.as_ref().parent().unwrap())?;
fs::write(config_file_path2.as_ref(), "dummy config")?;
let live_file = hc_dir.join(".hc_live_0");
fs::write(&live_file, "12345")?;
let paths = vec![config_path1, config_path2];
save(hc_dir.clone(), paths)?;
remove(
hc_dir.clone(),
Existing {
all: true,
indices: vec![],
},
)?;
assert!(!sandbox1.exists());
assert!(!sandbox2.exists());
let hc_file = hc_dir.join(".hc");
assert!(!hc_file.exists());
assert!(!live_file.exists());
Ok(())
}
#[test]
fn test_remove_with_missing_directories() -> anyhow::Result<()> {
let temp_dir = tempfile::tempdir()?;
let test_dir = temp_dir.path();
let hc_dir = test_dir.join("hc_dir");
let hc_file = hc_dir.join(".hc");
fs::create_dir_all(&hc_dir)?;
let sandbox1 = test_dir.join("sandbox1");
let sandbox2 = test_dir.join("sandbox2");
let sandbox3 = test_dir.join("sandbox3");
fs::create_dir_all(&sandbox1)?;
fs::create_dir_all(&sandbox2)?;
fs::create_dir_all(&sandbox3)?;
let config_path1 = ConfigRootPath::from(sandbox1.clone());
let config_file_path1 = ConfigFilePath::from(config_path1.clone());
fs::create_dir_all(config_file_path1.as_ref().parent().unwrap())?;
fs::write(config_file_path1.as_ref(), "dummy config")?;
let config_path2 = ConfigRootPath::from(sandbox2.clone());
let config_file_path2 = ConfigFilePath::from(config_path2.clone());
fs::create_dir_all(config_file_path2.as_ref().parent().unwrap())?;
fs::write(config_file_path2.as_ref(), "dummy config")?;
let config_path3 = ConfigRootPath::from(sandbox3.clone());
let config_file_path3 = ConfigFilePath::from(config_path3.clone());
fs::create_dir_all(config_file_path3.as_ref().parent().unwrap())?;
fs::write(config_file_path3.as_ref(), "dummy config")?;
let paths = vec![config_path1, config_path2, config_path3];
save(hc_dir.clone(), paths)?;
fs::remove_dir_all(&sandbox1)?;
remove(
hc_dir.clone(),
Existing {
all: false,
indices: vec![0],
},
)?;
assert!(!sandbox1.exists());
assert!(sandbox2.exists());
assert!(sandbox3.exists());
assert!(hc_file.exists());
let loaded_paths = load(hc_dir.clone())?;
assert_eq!(loaded_paths.len(), 2);
assert_eq!(loaded_paths[0], Ok(sandbox2.clone()));
assert_eq!(loaded_paths[1], Ok(sandbox3.clone()));
fs::remove_dir_all(&sandbox2)?;
remove(
hc_dir.clone(),
Existing {
all: false,
indices: vec![0, 1],
},
)?;
assert!(!sandbox2.exists());
assert!(!sandbox3.exists());
assert!(!hc_file.exists());
Ok(())
}
#[test]
fn test_remove_with_missing_config_file() -> anyhow::Result<()> {
let temp_dir = tempfile::tempdir()?;
let test_dir = temp_dir.path();
let hc_dir = test_dir.join("hc_dir");
let hc_file = hc_dir.join(".hc");
fs::create_dir_all(&hc_dir)?;
let sandbox1 = test_dir.join("sandbox1");
let sandbox2 = test_dir.join("sandbox2");
fs::create_dir_all(&sandbox1)?;
fs::create_dir_all(&sandbox2)?;
let config_path1 = ConfigRootPath::from(sandbox1.clone());
let config_file_path1 = ConfigFilePath::from(config_path1.clone());
fs::create_dir_all(config_file_path1.as_ref().parent().unwrap())?;
fs::write(config_file_path1.as_ref(), "dummy config")?;
let config_path2 = ConfigRootPath::from(sandbox2.clone());
let config_file_path2 = ConfigFilePath::from(config_path2.clone());
fs::create_dir_all(config_file_path2.as_ref().parent().unwrap())?;
fs::write(config_file_path2.as_ref(), "dummy config")?;
let paths = vec![config_path1, config_path2];
save(hc_dir.clone(), paths)?;
fs::remove_file(config_file_path1.as_ref())?;
remove(
hc_dir.clone(),
Existing {
all: false,
indices: vec![0],
},
)?;
assert!(!sandbox1.exists());
assert!(sandbox2.exists());
assert!(hc_file.exists());
Ok(())
}
#[test]
fn test_remove_nonexistent_sandbox_index() -> anyhow::Result<()> {
let temp_dir = tempfile::tempdir()?;
let test_dir = temp_dir.path();
let hc_dir = test_dir.join("hc_dir");
let hc_file = hc_dir.join(".hc");
fs::create_dir_all(&hc_dir)?;
let sandbox1 = test_dir.join("sandbox1");
let sandbox2 = test_dir.join("sandbox2");
fs::create_dir_all(&sandbox1)?;
fs::create_dir_all(&sandbox2)?;
let config_path1 = ConfigRootPath::from(sandbox1.clone());
let config_file_path1 = ConfigFilePath::from(config_path1.clone());
fs::create_dir_all(config_file_path1.as_ref().parent().unwrap())?;
fs::write(config_file_path1.as_ref(), "dummy config")?;
let config_path2 = ConfigRootPath::from(sandbox2.clone());
let config_file_path2 = ConfigFilePath::from(config_path2.clone());
fs::create_dir_all(config_file_path2.as_ref().parent().unwrap())?;
fs::write(config_file_path2.as_ref(), "dummy config")?;
let paths = vec![config_path1, config_path2];
save(hc_dir.clone(), paths)?;
remove(
hc_dir.clone(),
Existing {
all: false,
indices: vec![2],
},
)?;
assert!(sandbox1.exists());
assert!(sandbox2.exists());
assert!(hc_file.exists());
remove(
hc_dir.clone(),
Existing {
all: false,
indices: vec![0, 2],
},
)?;
assert!(!sandbox1.exists());
assert!(sandbox2.exists());
assert!(hc_file.exists());
let loaded_paths = load(hc_dir.clone())?;
assert_eq!(loaded_paths.len(), 1);
assert_eq!(loaded_paths[0], Ok(sandbox2.clone()));
Ok(())
}
#[test]
fn test_list_with_valid_paths() -> anyhow::Result<()> {
let temp_dir = tempfile::tempdir()?;
let test_dir = temp_dir.path();
let hc_dir = test_dir.join("hc_dir");
fs::create_dir_all(&hc_dir)?;
let sandbox1 = test_dir.join("sandbox1");
let sandbox2 = test_dir.join("sandbox2");
fs::create_dir_all(&sandbox1)?;
fs::create_dir_all(&sandbox2)?;
let config_path1 = ConfigRootPath::from(sandbox1.clone());
let config_file_path1 = ConfigFilePath::from(config_path1.clone());
fs::create_dir_all(config_file_path1.as_ref().parent().unwrap())?;
fs::write(config_file_path1.as_ref(), "dummy config")?;
let config_path2 = ConfigRootPath::from(sandbox2.clone());
let config_file_path2 = ConfigFilePath::from(config_path2.clone());
fs::create_dir_all(config_file_path2.as_ref().parent().unwrap())?;
fs::write(config_file_path2.as_ref(), "dummy config")?;
let paths = vec![config_path1, config_path2];
save(hc_dir.clone(), paths)?;
list(hc_dir.clone(), false)?;
Ok(())
}
#[test]
fn test_list_with_verbose() -> anyhow::Result<()> {
let temp_dir = tempfile::tempdir()?;
let test_dir = temp_dir.path();
let hc_dir = test_dir.join("hc_dir");
fs::create_dir_all(&hc_dir)?;
let sandbox1 = test_dir.join("sandbox1");
fs::create_dir_all(&sandbox1)?;
let config_root_path = ConfigRootPath::from(sandbox1.clone());
let config =
holochain_conductor_config::config::create_config(config_root_path.clone(), None)?;
holochain_conductor_config::config::write_config(config_root_path.clone(), &config)?;
let paths = vec![config_root_path];
save(hc_dir.clone(), paths)?;
list(hc_dir.clone(), true)?;
Ok(())
}
#[test]
fn test_list_with_missing_paths() -> anyhow::Result<()> {
let temp_dir = tempfile::tempdir()?;
let test_dir = temp_dir.path();
let hc_dir = test_dir.join("hc_dir");
fs::create_dir_all(&hc_dir)?;
let sandbox1 = test_dir.join("sandbox1");
let sandbox2 = test_dir.join("sandbox2");
fs::create_dir_all(&sandbox1)?;
fs::create_dir_all(&sandbox2)?;
let config_path1 = ConfigRootPath::from(sandbox1.clone());
let config_file_path1 = ConfigFilePath::from(config_path1.clone());
fs::create_dir_all(config_file_path1.as_ref().parent().unwrap())?;
fs::write(config_file_path1.as_ref(), "dummy config")?;
let config_path2 = ConfigRootPath::from(sandbox2.clone());
let config_file_path2 = ConfigFilePath::from(config_path2.clone());
fs::create_dir_all(config_file_path2.as_ref().parent().unwrap())?;
fs::write(config_file_path2.as_ref(), "dummy config")?;
let paths = vec![config_path1, config_path2];
save(hc_dir.clone(), paths)?;
fs::remove_dir_all(&sandbox2)?;
list(hc_dir.clone(), false)?;
Ok(())
}
#[test]
fn test_list_empty_directory() -> anyhow::Result<()> {
let temp_dir = tempfile::tempdir()?;
let test_dir = temp_dir.path();
let hc_dir = test_dir.join("hc_dir");
fs::create_dir_all(&hc_dir)?;
list(hc_dir.clone(), false)?;
Ok(())
}
}