tsafe-cli 1.0.23

Local-first developer secret vault CLI — encrypted storage, process injection via exec, cloud sync, audit trail
Documentation
//! Integration tests for `tsafe export --format toml` (EC-3).

use assert_cmd::Command;
use tempfile::tempdir;

fn tsafe() -> Command {
    Command::cargo_bin("tsafe").unwrap()
}

fn init_vault(dir: &std::path::Path) {
    tsafe()
        .args(["init"])
        .env("TSAFE_VAULT_DIR", dir)
        .env("TSAFE_PASSWORD", "test-pw")
        .assert()
        .success();
}

#[test]
fn export_toml_produces_flat_key_value_pairs() {
    let dir = tempdir().unwrap();
    init_vault(dir.path());

    tsafe()
        .args(["set", "DB_HOST", "localhost"])
        .env("TSAFE_VAULT_DIR", dir.path())
        .env("TSAFE_PASSWORD", "test-pw")
        .assert()
        .success();

    tsafe()
        .args(["set", "API_KEY", "super-secret"])
        .env("TSAFE_VAULT_DIR", dir.path())
        .env("TSAFE_PASSWORD", "test-pw")
        .assert()
        .success();

    let out = tsafe()
        .args(["export", "--format", "toml"])
        .env("TSAFE_VAULT_DIR", dir.path())
        .env("TSAFE_PASSWORD", "test-pw")
        .assert()
        .success()
        .get_output()
        .stdout
        .clone();

    let text = String::from_utf8(out).unwrap();
    // Both keys should appear as TOML assignments
    assert!(
        text.contains("DB_HOST = \"localhost\""),
        "expected DB_HOST = \"localhost\" in:\n{text}"
    );
    assert!(
        text.contains("API_KEY = \"super-secret\""),
        "expected API_KEY = \"super-secret\" in:\n{text}"
    );
    // No TOML section headers
    assert!(
        !text.contains('['),
        "unexpected '[' (section header) in TOML output:\n{text}"
    );
}

#[test]
fn export_toml_escapes_backslash_and_double_quote_in_values() {
    let dir = tempdir().unwrap();
    init_vault(dir.path());

    // Value contains both a backslash and a double-quote
    tsafe()
        .args(["set", "TRICKY", r#"path\to\"file""#])
        .env("TSAFE_VAULT_DIR", dir.path())
        .env("TSAFE_PASSWORD", "test-pw")
        .assert()
        .success();

    let out = tsafe()
        .args(["export", "--format", "toml"])
        .env("TSAFE_VAULT_DIR", dir.path())
        .env("TSAFE_PASSWORD", "test-pw")
        .assert()
        .success()
        .get_output()
        .stdout
        .clone();

    let text = String::from_utf8(out).unwrap();
    // Backslash must be doubled; double-quote must be escaped
    assert!(
        text.contains(r#"TRICKY = "path\\to\\\"file\"""#),
        "unexpected escaping in TOML output:\n{text}"
    );
}

#[test]
fn export_toml_output_is_sorted_by_key() {
    let dir = tempdir().unwrap();
    init_vault(dir.path());

    for key in ["ZZZ", "AAA", "MMM"] {
        tsafe()
            .args(["set", key, "v"])
            .env("TSAFE_VAULT_DIR", dir.path())
            .env("TSAFE_PASSWORD", "test-pw")
            .assert()
            .success();
    }

    let out = tsafe()
        .args(["export", "--format", "toml"])
        .env("TSAFE_VAULT_DIR", dir.path())
        .env("TSAFE_PASSWORD", "test-pw")
        .assert()
        .success()
        .get_output()
        .stdout
        .clone();

    let text = String::from_utf8(out).unwrap();
    let lines: Vec<&str> = text.lines().collect();
    let keys: Vec<&str> = lines
        .iter()
        .map(|l| l.split(" = ").next().unwrap_or(""))
        .collect();
    let mut sorted = keys.clone();
    sorted.sort_unstable();
    assert_eq!(keys, sorted, "TOML output must be sorted by key");
}