use assert_cmd::Command;
use predicates::prelude::*;
use std::fs;
use std::path::PathBuf;
fn with_home<F: FnOnce(PathBuf)>(f: F) {
let tmp = tempfile::tempdir().unwrap();
let home = tmp.path().to_path_buf();
f(home);
}
#[test]
fn put_and_get_key() {
with_home(|home| {
let mut cmd = Command::cargo_bin("unifier").unwrap();
cmd.args([
"--home",
&home.display().to_string(),
"put",
"app/theme",
"dark",
])
.assert()
.success();
Command::cargo_bin("unifier")
.unwrap()
.args(["--home", &home.display().to_string(), "get", "app/theme"])
.assert()
.success()
.stdout("dark\n");
});
}
#[test]
fn send_and_poll_mailbox() {
with_home(|home| {
let home_s = home.display().to_string();
Command::cargo_bin("unifier")
.unwrap()
.args(["--home", &home_s, "send", "worker", "hello"])
.assert()
.success();
Command::cargo_bin("unifier")
.unwrap()
.args(["--home", &home_s, "poll", "worker"])
.assert()
.success()
.stdout(predicate::str::contains("hello"));
});
}
fn home_args(home: &str) -> Vec<String> {
vec!["--home".into(), home.into(), "--no-daemon".into()]
}
#[test]
fn cron_post_and_list() {
with_home(|home| {
let home_s = home.display().to_string();
let mut args = home_args(&home_s);
args.extend(["cron".into(), "0_0_*_*_*".into(), "nightly backup".into()]);
Command::cargo_bin("unifier")
.unwrap()
.args(&args)
.assert()
.success();
let mut list_args = home_args(&home_s);
list_args.extend(["list".into(), "cron/0_0_*_*_*".into()]);
Command::cargo_bin("unifier")
.unwrap()
.args(&list_args)
.assert()
.success()
.stdout(predicate::str::contains("nightly backup"));
assert!(home.join("cron/0_0_*_*_*").is_dir());
let entries: Vec<_> = fs::read_dir(home.join("cron/0_0_*_*_*"))
.unwrap()
.map(|e| e.unwrap().path())
.collect();
assert_eq!(entries.len(), 1);
assert!(entries[0].extension().unwrap() == "txt");
});
}
#[test]
fn root_prints_home() {
with_home(|home| {
Command::cargo_bin("unifier")
.unwrap()
.args(["--home", &home.display().to_string(), "root"])
.assert()
.success()
.stdout(format!("{}\n", home.display()));
});
}
#[test]
fn chroot_isolates_keys() {
with_home(|home| {
let home_s = home.display().to_string();
Command::cargo_bin("unifier")
.unwrap()
.args(["--home", &home_s, "chroot", "init", "work"])
.assert()
.success();
Command::cargo_bin("unifier")
.unwrap()
.args(["--home", &home_s, "--chroot", "work", "put", "k", "in-work"])
.assert()
.success();
Command::cargo_bin("unifier")
.unwrap()
.args(["--home", &home_s, "--chroot", "work", "get", "k"])
.assert()
.success()
.stdout("in-work\n");
Command::cargo_bin("unifier")
.unwrap()
.args(["--home", &home_s, "get", "k"])
.assert()
.failure();
Command::cargo_bin("unifier")
.unwrap()
.args(["--home", &home_s, "chroot", "init", "personal"])
.assert()
.success();
Command::cargo_bin("unifier")
.unwrap()
.args(["--home", &home_s, "--chroot", "personal", "get", "k"])
.assert()
.failure();
});
}
#[test]
fn chroot_root_shows_effective_path() {
with_home(|home| {
let home_s = home.display().to_string();
Command::cargo_bin("unifier")
.unwrap()
.args(["--home", &home_s, "chroot", "init", "work"])
.assert()
.success();
let expected = format!("{}/chroots/work\n", home.display());
Command::cargo_bin("unifier")
.unwrap()
.args(["--home", &home_s, "--chroot", "work", "root"])
.assert()
.success()
.stdout(expected);
});
}
#[test]
fn chroot_list_names() {
with_home(|home| {
let home_s = home.display().to_string();
Command::cargo_bin("unifier")
.unwrap()
.args(["--home", &home_s, "chroot", "init", "work"])
.assert()
.success();
Command::cargo_bin("unifier")
.unwrap()
.args(["--home", &home_s, "chroot", "init", "personal"])
.assert()
.success();
Command::cargo_bin("unifier")
.unwrap()
.args(["--home", &home_s, "chroot", "list"])
.assert()
.success()
.stdout("personal\nwork\n");
});
}
#[test]
fn list_rejects_path_escape() {
with_home(|home| {
let home_s = home.display().to_string();
let mut args = home_args(&home_s);
args.extend(["list".into(), "../keys".into()]);
Command::cargo_bin("unifier")
.unwrap()
.args(&args)
.assert()
.failure()
.stderr(predicate::str::contains(".."));
});
}
#[test]
fn namespace_flag_prefixes_keys() {
with_home(|home| {
let home_s = home.display().to_string();
let mut put = home_args(&home_s);
put.extend([
"--namespace".into(),
"job".into(),
"put".into(),
"theme".into(),
"dark".into(),
]);
Command::cargo_bin("unifier")
.unwrap()
.args(&put)
.assert()
.success();
assert!(home.join("keys/job/theme").is_file());
assert!(!home.join("keys/theme").exists());
let mut get = home_args(&home_s);
get.extend([
"--namespace".into(),
"job".into(),
"get".into(),
"theme".into(),
]);
Command::cargo_bin("unifier")
.unwrap()
.args(&get)
.assert()
.success()
.stdout("dark\n");
let mut miss = home_args(&home_s);
miss.extend(["get".into(), "theme".into()]);
Command::cargo_bin("unifier")
.unwrap()
.args(&miss)
.assert()
.failure();
});
}
#[test]
fn namespace_set_applies_until_clear() {
with_home(|home| {
let home_s = home.display().to_string();
Command::cargo_bin("unifier")
.unwrap()
.args([
"--home",
&home_s,
"--no-daemon",
"namespace",
"set",
"myscript",
])
.assert()
.success()
.stdout("myscript\n");
Command::cargo_bin("unifier")
.unwrap()
.args(["--home", &home_s, "--no-daemon", "put", "app/theme", "dark"])
.assert()
.success();
assert!(home.join("keys/myscript/app/theme").is_file());
Command::cargo_bin("unifier")
.unwrap()
.args(["--home", &home_s, "--no-daemon", "get", "app/theme"])
.assert()
.success()
.stdout("dark\n");
Command::cargo_bin("unifier")
.unwrap()
.args([
"--home",
&home_s,
"--no-daemon",
"get",
"/myscript/app/theme",
])
.assert()
.success()
.stdout("dark\n");
Command::cargo_bin("unifier")
.unwrap()
.args(["--home", &home_s, "--no-daemon", "namespace", "get"])
.assert()
.success()
.stdout("myscript\n");
Command::cargo_bin("unifier")
.unwrap()
.args(["--home", &home_s, "--no-daemon", "namespace", "clear"])
.assert()
.success();
Command::cargo_bin("unifier")
.unwrap()
.args(["--home", &home_s, "--no-daemon", "get", "app/theme"])
.assert()
.failure();
Command::cargo_bin("unifier")
.unwrap()
.args([
"--home",
&home_s,
"--no-daemon",
"get",
"/myscript/app/theme",
])
.assert()
.success()
.stdout("dark\n");
});
}
#[test]
fn namespace_set_replaced_and_flag_overrides() {
with_home(|home| {
let home_s = home.display().to_string();
Command::cargo_bin("unifier")
.unwrap()
.args(["--home", &home_s, "--no-daemon", "namespace", "set", "one"])
.assert()
.success();
Command::cargo_bin("unifier")
.unwrap()
.args(["--home", &home_s, "--no-daemon", "namespace", "set", "two"])
.assert()
.success();
Command::cargo_bin("unifier")
.unwrap()
.args(["--home", &home_s, "--no-daemon", "put", "k", "from-two"])
.assert()
.success();
assert!(home.join("keys/two/k").is_file());
Command::cargo_bin("unifier")
.unwrap()
.args([
"--home",
&home_s,
"--no-daemon",
"--namespace",
"other",
"put",
"k",
"from-other",
])
.assert()
.success();
assert!(home.join("keys/other/k").is_file());
Command::cargo_bin("unifier")
.unwrap()
.args(["--home", &home_s, "--no-daemon", "get", "k"])
.assert()
.success()
.stdout("from-two\n");
});
}
#[test]
fn descendant_process_inherits_namespace() {
with_home(|home| {
let home_s = home.display().to_string();
let bin = assert_cmd::cargo::cargo_bin("unifier");
Command::cargo_bin("unifier")
.unwrap()
.args(["--home", &home_s, "--no-daemon", "namespace", "set", "job"])
.assert()
.success();
let status = std::process::Command::new("sh")
.arg("-c")
.arg(format!(
"{} --home {} --no-daemon put theme nested",
shell_single_quote(&bin.display().to_string()),
shell_single_quote(&home_s)
))
.status()
.unwrap();
assert!(status.success());
assert!(home.join("keys/job/theme").is_file());
});
}
fn shell_single_quote(s: &str) -> String {
format!("'{}'", s.replace('\'', "'\"'\"'"))
}