revault_cli 0.0.3

CLI for reVault encrypted lockboxes, store files, variables and forms with integration to your platform key storage
mod common;

use common::TestTempDir;
use std::fs;
use std::process::Command;

fn run(bin: &str, args: &[&str], vault_dir: &std::path::Path) -> std::process::Output {
    Command::new(bin)
        .args(args)
        .env("LOCKBOX_VAULT_DIR", vault_dir)
        .env_remove("LOCKBOX_VAULT_PASSWORD")
        .env_remove("COMPLETE")
        .output()
        .unwrap()
}

#[test]
fn completion_generation_supports_shell_override_and_install_uninstall() {
    let bin = env!("CARGO_BIN_EXE_lockbox");
    let temp = TestTempDir::new("completion-install");
    let vault_dir = temp.path().join("vault");
    let output_path = temp.path().join("completion").join("lockbox.bash");

    let generated = run(
        bin,
        &[
            "completion",
            "generate",
            "--shell",
            "bash",
            "--output",
            output_path.to_str().unwrap(),
        ],
        &vault_dir,
    );
    assert!(generated.status.success(), "{generated:?}");
    let script = fs::read_to_string(&output_path).unwrap();
    assert!(script.contains("_clap_complete_lockbox"));
    assert!(script.contains("COMPLETE=\"bash\""));

    let installed = temp.path().join("custom").join("lockbox.fish");
    let output = run(
        bin,
        &[
            "completion",
            "install",
            "--shell",
            "fish",
            "--path",
            installed.to_str().unwrap(),
        ],
        &vault_dir,
    );
    assert!(output.status.success(), "{output:?}");
    assert!(installed.exists());

    let output = run(
        bin,
        &[
            "completion",
            "uninstall",
            "--shell",
            "fish",
            "--path",
            installed.to_str().unwrap(),
        ],
        &vault_dir,
    );
    assert!(output.status.success(), "{output:?}");
    assert!(!installed.exists());
}

#[test]
fn locked_vault_completion_falls_back_without_prompt_or_diagnostics() {
    let bin = env!("CARGO_BIN_EXE_lockbox");
    let temp = TestTempDir::new("completion-locked");
    let vault_dir = temp.path().join("vault");

    let output = Command::new(bin)
        .env("LOCKBOX_VAULT_DIR", &vault_dir)
        .env("COMPLETE", "bash")
        .env("_CLAP_COMPLETE_INDEX", "4")
        .env("_CLAP_COMPLETE_COMP_TYPE", "9")
        .env("_CLAP_COMPLETE_SPACE", "true")
        .args(["--", "lockbox", "vault", "profile", "create", ""])
        .output()
        .unwrap();
    assert!(output.status.success(), "{output:?}");
    assert!(output.stderr.is_empty(), "{:?}", output.stderr);
}

#[test]
fn dynamic_completion_reads_vault_names_without_exposing_signing_material() {
    let bin = env!("CARGO_BIN_EXE_lockbox");
    let temp = TestTempDir::new("completion-dynamic");
    let vault_dir = temp.path().join("vault");
    let agent_dir = temp.path().join("agent");
    let agent_log = temp.path().join("agent.log");
    let password = "completion-test-vault-password";

    let init = Command::new(bin)
        .args(["vault", "init"])
        .env("LOCKBOX_VAULT_DIR", &vault_dir)
        .env("LOCKBOX_VAULT_PASSWORD", password)
        .env("LOCKBOX_SESSION_AGENT_DIR", &agent_dir)
        .env("LOCKBOX_SESSION_AGENT_LOG", &agent_log)
        .output()
        .unwrap();
    assert!(init.status.success(), "{init:?}");

    let create = Command::new(bin)
        .args(["vault", "profile", "create", "alice"])
        .env("LOCKBOX_VAULT_DIR", &vault_dir)
        .env("LOCKBOX_VAULT_PASSWORD", password)
        .env("LOCKBOX_SESSION_AGENT_DIR", &agent_dir)
        .env("LOCKBOX_SESSION_AGENT_LOG", &agent_log)
        .output()
        .unwrap();
    assert!(create.status.success(), "{create:?}");

    let log_len_before_completion = fs::metadata(&agent_log).map(|m| m.len()).unwrap_or(0);

    let output = Command::new(bin)
        .env("LOCKBOX_VAULT_DIR", &vault_dir)
        .env("LOCKBOX_VAULT_PASSWORD", password)
        .env("LOCKBOX_SESSION_AGENT_DIR", &agent_dir)
        .env("LOCKBOX_SESSION_AGENT_LOG", &agent_log)
        .env("COMPLETE", "bash")
        .env("_CLAP_COMPLETE_INDEX", "4")
        .env("_CLAP_COMPLETE_COMP_TYPE", "9")
        .env("_CLAP_COMPLETE_SPACE", "true")
        .args(["--", "lockbox", "vault", "profile", "history", "al"])
        .output()
        .unwrap();
    assert!(output.status.success(), "{output:?}");
    assert!(String::from_utf8_lossy(&output.stdout).contains("alice"));
    assert!(!String::from_utf8_lossy(&output.stdout).contains("LBX1SPRV"));
    assert!(output.stderr.is_empty(), "{:?}", output.stderr);

    let agent_log_bytes = fs::read(&agent_log).unwrap_or_default();
    let completion_log = &agent_log_bytes[log_len_before_completion as usize..];
    assert!(
        !String::from_utf8_lossy(completion_log).contains("owner-signing:"),
        "completion must not request an owner-signing key from the agent"
    );
}