use assert_cmd::Command;
use predicates::prelude::*;
use std::fs;
use std::sync::Mutex;
use tempfile::TempDir;
static HOME_MUTEX: Mutex<()> = Mutex::new(());
#[test]
fn test_cli_version() {
let cargo_toml = include_str!("../Cargo.toml");
let version = cargo_toml
.lines()
.find(|line| line.starts_with("version = "))
.and_then(|line| line.split('"').nth(1))
.expect("Version not found in Cargo.toml");
let mut cmd = Command::cargo_bin("bv").unwrap();
cmd.arg("--version")
.assert()
.success()
.stdout(predicate::str::contains(version));
}
#[test]
fn test_cli_help() {
let mut cmd = Command::cargo_bin("bv").unwrap();
cmd.arg("--help")
.assert()
.success()
.stdout(predicate::str::contains("BioVault"))
.stdout(predicate::str::contains("init"))
.stdout(predicate::str::contains("info"))
.stdout(predicate::str::contains("check"));
}
#[test]
fn test_init_command() {
let _guard = HOME_MUTEX
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner());
let temp_dir = TempDir::new().unwrap();
let config_dir = temp_dir.path().join(".biovault");
let original_home = if cfg!(windows) {
let original_profile = std::env::var("USERPROFILE").ok();
std::env::set_var("USERPROFILE", temp_dir.path());
original_profile
} else {
let original_home = std::env::var("HOME").ok();
std::env::set_var("HOME", temp_dir.path());
original_home
};
let mut cmd = Command::cargo_bin("bv").unwrap();
cmd.env("BIOVAULT_TEST_HOME", temp_dir.path());
if cfg!(windows) {
cmd.env("USERPROFILE", temp_dir.path());
cmd.env_remove("HOMEDRIVE");
cmd.env_remove("HOMEPATH");
} else {
cmd.env("HOME", temp_dir.path());
}
cmd.arg("init")
.arg("test@example.com")
.assert()
.success()
.stdout(predicate::str::contains(
"BioVault initialized successfully",
));
let config_file = config_dir.join("config.yaml");
assert!(
config_file.exists(),
"Config file should exist at: {}",
config_file.display()
);
let contents = fs::read_to_string(&config_file).unwrap();
assert!(contents.contains("email: test@example.com"));
if cfg!(windows) {
if let Some(home) = original_home {
std::env::set_var("USERPROFILE", home);
} else {
std::env::remove_var("USERPROFILE");
}
} else if let Some(home) = original_home {
std::env::set_var("HOME", home);
} else {
std::env::remove_var("HOME");
}
}
#[test]
fn test_init_command_existing_config() {
let _guard = HOME_MUTEX
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner());
let temp_dir = TempDir::new().unwrap();
let config_dir = temp_dir.path().join(".biovault");
fs::create_dir_all(&config_dir).unwrap();
let config_file = config_dir.join("config.yaml");
fs::write(&config_file, "email: existing@example.com\n").unwrap();
let original_home = if cfg!(windows) {
let original_profile = std::env::var("USERPROFILE").ok();
std::env::set_var("USERPROFILE", temp_dir.path());
original_profile
} else {
let original_home = std::env::var("HOME").ok();
std::env::set_var("HOME", temp_dir.path());
original_home
};
let mut cmd = Command::cargo_bin("bv").unwrap();
cmd.env("BIOVAULT_TEST_HOME", temp_dir.path());
if cfg!(windows) {
cmd.env("USERPROFILE", temp_dir.path());
cmd.env_remove("HOMEDRIVE");
cmd.env_remove("HOMEPATH");
} else {
cmd.env("HOME", temp_dir.path());
}
cmd.arg("init")
.arg("new@example.com")
.assert()
.success()
.stdout(predicate::str::contains("already exists"));
let contents = fs::read_to_string(&config_file).unwrap();
assert!(contents.contains("email: existing@example.com"));
assert!(!contents.contains("email: new@example.com"));
if cfg!(windows) {
if let Some(home) = original_home {
std::env::set_var("USERPROFILE", home);
} else {
std::env::remove_var("USERPROFILE");
}
} else if let Some(home) = original_home {
std::env::set_var("HOME", home);
} else {
std::env::remove_var("HOME");
}
}
#[test]
fn test_info_command() {
let mut cmd = Command::cargo_bin("bv").unwrap();
cmd.arg("info")
.assert()
.success()
.stdout(predicate::str::contains("System Information"))
.stdout(predicate::str::contains("OS:"))
.stdout(predicate::str::contains("CPU Arch:"))
.stdout(predicate::str::contains("CPUs:"))
.stdout(predicate::str::contains("RAM:"))
.stdout(predicate::str::contains("DISK FREE:"));
}
#[test]
fn test_project_examples_cli() {
let mut cmd = Command::cargo_bin("bv").unwrap();
cmd.arg("project")
.arg("examples")
.assert()
.success()
.stdout(predicate::str::contains("Available example templates"));
}
#[test]
fn test_sample_data_list_cli() {
let mut cmd = Command::cargo_bin("bv").unwrap();
cmd.arg("sample-data")
.arg("list")
.assert()
.success()
.stdout(predicate::str::contains("Available sample data"));
}
#[test]
fn test_run_dry_run_cli() {
let tmp = TempDir::new().unwrap();
let bv_home = tmp.path().join(".bvhome");
fs::create_dir_all(bv_home.join("env/test_tpl")).unwrap();
fs::write(bv_home.join("env/test_tpl/template.nf"), "// template").unwrap();
fs::write(bv_home.join("env/test_tpl/nextflow.config"), "// config").unwrap();
let proj = tmp.path().join("proj");
fs::create_dir_all(&proj).unwrap();
fs::write(
proj.join("project.yaml"),
"name: p\nauthor: a\nworkflow: main.nf\ntemplate: test_tpl\n",
)
.unwrap();
fs::write(proj.join("workflow.nf"), "// wf").unwrap();
fs::write(
proj.join("participants.yaml"),
"participants:\n X:\n ref_version: GRCh38\n",
)
.unwrap();
let mut cmd = Command::cargo_bin("bv").unwrap();
cmd.env("BIOVAULT_HOME", &bv_home)
.arg("run")
.arg(proj.to_string_lossy().to_string())
.arg(
proj.join("participants.yaml#participants.X")
.to_string_lossy()
.to_string(),
)
.arg("--dry-run")
.arg("--template=test_tpl")
.assert()
.success()
.stdout(predicate::str::contains("Nextflow command:"));
}
#[test]
fn test_invalid_command() {
let mut cmd = Command::cargo_bin("bv").unwrap();
cmd.arg("invalid-command")
.assert()
.failure()
.stderr(predicate::str::contains("unrecognized subcommand"));
}
#[test]
fn test_init_missing_email() {
let mut cmd = Command::cargo_bin("bv").unwrap();
cmd.arg("init")
.assert()
.failure()
.stderr(predicate::str::contains("required"));
}