unifier-cli 0.2.0

Filesystem postbox for inter-process communication via a Unix tree
Documentation
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(".."));
    });
}