mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-07 08:27:12 +00:00
Wrap root CLI errors at the main boundary so fatal diagnostics use miette's styled renderer while preserving existing telemetry, exit codes, and auth help hints.
165 lines
4.5 KiB
Rust
165 lines
4.5 KiB
Rust
#![allow(
|
||
clippy::absolute_paths,
|
||
clippy::single_char_pattern,
|
||
reason = "These secret-command tests use terse fixture patterns and explicit paths."
|
||
)]
|
||
|
||
use fabro_test::{fabro_snapshot, test_context};
|
||
use predicates::prelude::*;
|
||
|
||
#[test]
|
||
fn help() {
|
||
let context = test_context!();
|
||
let mut cmd = context.secret();
|
||
cmd.arg("--help");
|
||
fabro_snapshot!(context.filters(), cmd, @"
|
||
success: true
|
||
exit_code: 0
|
||
----- stdout -----
|
||
Manage server-owned secrets
|
||
|
||
Usage: fabro secret [OPTIONS] <COMMAND>
|
||
|
||
Commands:
|
||
list List secret names
|
||
rm Remove a secret
|
||
set Set a secret value
|
||
help Print this message or the help of the given subcommand(s)
|
||
|
||
Options:
|
||
--json Output as JSON [env: FABRO_JSON=]
|
||
--server <SERVER> Fabro server target: http(s) URL or absolute Unix socket path [env: FABRO_SERVER=]
|
||
--debug Enable DEBUG-level logging (default is INFO) [env: FABRO_DEBUG=]
|
||
--no-upgrade-check Disable automatic upgrade check [env: FABRO_NO_UPGRADE_CHECK=true]
|
||
--quiet Suppress non-essential output [env: FABRO_QUIET=]
|
||
--verbose Enable verbose output [env: FABRO_VERBOSE=]
|
||
-h, --help Print help
|
||
----- stderr -----
|
||
");
|
||
}
|
||
|
||
#[test]
|
||
fn test_secret_lifecycle() {
|
||
let context = test_context!();
|
||
|
||
let secret =
|
||
|args: &[&str]| -> assert_cmd::assert::Assert { context.secret().args(args).assert() };
|
||
|
||
// 1. set FOO=bar
|
||
secret(&["set", "FOO", "bar"]).success();
|
||
|
||
// 2. list -> contains FOO
|
||
secret(&["list"])
|
||
.success()
|
||
.stdout(predicates::str::contains("FOO"))
|
||
.stdout(predicates::str::contains("environment"));
|
||
|
||
// 3. update FOO
|
||
secret(&["set", "FOO", "updated"]).success();
|
||
|
||
// 4. rm FOO
|
||
secret(&["rm", "FOO"]).success();
|
||
|
||
// 5. list no longer contains FOO
|
||
let output = secret(&["list"]).success().get_output().stdout.clone();
|
||
let stdout = String::from_utf8(output).unwrap();
|
||
assert!(!stdout.contains("FOO"));
|
||
}
|
||
|
||
#[test]
|
||
fn test_secret_list_is_write_only() {
|
||
let context = test_context!();
|
||
|
||
let secret =
|
||
|args: &[&str]| -> assert_cmd::assert::Assert { context.secret().args(args).assert() };
|
||
|
||
secret(&["set", "A", "alpha-secret"]).success();
|
||
secret(&["set", "B", "beta-secret"]).success();
|
||
|
||
let out = secret(&["list"]).success();
|
||
let stdout = String::from_utf8(out.get_output().stdout.clone()).unwrap();
|
||
assert!(stdout.contains("A"));
|
||
assert!(stdout.contains("B"));
|
||
assert!(!stdout.contains("alpha-secret"));
|
||
assert!(!stdout.contains("beta-secret"));
|
||
}
|
||
|
||
#[test]
|
||
fn test_secret_list_alias_ls() {
|
||
let context = test_context!();
|
||
|
||
context.secret().args(["set", "X", "y"]).assert().success();
|
||
|
||
context
|
||
.secret()
|
||
.args(["ls"])
|
||
.assert()
|
||
.success()
|
||
.stdout(predicates::str::contains("X"))
|
||
.stdout(predicates::str::contains("environment"));
|
||
}
|
||
|
||
#[test]
|
||
fn test_secret_rm_missing_key() {
|
||
let context = test_context!();
|
||
let mut cmd = context.secret();
|
||
cmd.args(["rm", "NOPE"]);
|
||
fabro_snapshot!(context.filters(), cmd, @"
|
||
success: false
|
||
exit_code: 1
|
||
----- stdout -----
|
||
----- stderr -----
|
||
× secret not found: NOPE
|
||
");
|
||
}
|
||
|
||
#[test]
|
||
fn test_secret_value_with_equals() {
|
||
let context = test_context!();
|
||
|
||
context
|
||
.secret()
|
||
.args(["set", "URL", "https://x.com?a=1&b=2"])
|
||
.assert()
|
||
.success();
|
||
|
||
context
|
||
.secret()
|
||
.args(["list"])
|
||
.assert()
|
||
.success()
|
||
.stdout(predicates::str::contains("URL"))
|
||
.stdout(predicates::str::contains("environment"))
|
||
.stdout(predicates::str::contains("https://x.com?a=1&b=2").not());
|
||
}
|
||
|
||
#[test]
|
||
fn test_file_secret_lifecycle() {
|
||
let context = test_context!();
|
||
|
||
let secret =
|
||
|args: &[&str]| -> assert_cmd::assert::Assert { context.secret().args(args).assert() };
|
||
|
||
secret(&[
|
||
"set",
|
||
"/tmp/test.pem",
|
||
"pem-data",
|
||
"--type",
|
||
"file",
|
||
"--description",
|
||
"Test certificate",
|
||
])
|
||
.success();
|
||
|
||
secret(&["list"])
|
||
.success()
|
||
.stdout(predicates::str::contains("/tmp/test.pem"))
|
||
.stdout(predicates::str::contains("file"))
|
||
.stdout(predicates::str::contains("pem-data").not());
|
||
|
||
secret(&["rm", "/tmp/test.pem"]).success();
|
||
|
||
secret(&["list"])
|
||
.success()
|
||
.stdout(predicates::str::contains("/tmp/test.pem").not());
|
||
}
|