use std::process::{Command, Output};
fn run(args: &[&str]) -> Output {
Command::new(env!("CARGO_BIN_EXE_freshdock"))
.args(args)
.output()
.expect("spawn freshdock binary")
}
fn run_with_env(args: &[&str], env: &[(&str, &str)]) -> Output {
let mut cmd = Command::new(env!("CARGO_BIN_EXE_freshdock"));
cmd.args(args);
for (key, value) in env {
cmd.env(key, value);
}
cmd.output().expect("spawn freshdock binary")
}
fn temp_config(name: &str, body: &str) -> std::path::PathBuf {
let path = std::env::temp_dir().join(format!("freshdock-cli-{name}.toml"));
std::fs::write(&path, body).expect("write temp config");
path
}
#[test]
fn help_lists_the_subcommands() {
let out = run(&["--help"]);
assert!(out.status.success(), "--help should exit 0");
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(stdout.contains("check"), "help missing `check`: {stdout}");
assert!(
stdout.contains("recreate"),
"help missing `recreate`: {stdout}"
);
assert!(stdout.contains("run"), "help missing `run`: {stdout}");
}
#[test]
fn version_exits_zero() {
let out = run(&["--version"]);
assert!(out.status.success(), "--version should exit 0");
assert!(
String::from_utf8_lossy(&out.stdout).contains("freshdock"),
"version output should name the binary"
);
}
#[test]
fn run_help_documents_flag_env_vars() {
let out = run(&["run", "--help"]);
assert!(out.status.success(), "run --help should exit 0");
let stdout = String::from_utf8_lossy(&out.stdout);
for var in [
"FRESHDOCK_INTERVAL",
"FRESHDOCK_TICK",
"FRESHDOCK_STOP_TIMEOUT",
] {
assert!(stdout.contains(var), "run --help missing {var}: {stdout}");
}
}
#[test]
fn invalid_interval_env_is_a_clap_error() {
let out = run_with_env(&["run"], &[("FRESHDOCK_INTERVAL", "abc")]);
assert!(!out.status.success(), "a non-numeric interval must error");
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(
stderr.contains("invalid value"),
"expected a clap parse error, got: {stderr}"
);
}
#[test]
fn malformed_config_fails_at_parse_before_docker() {
let cfg = temp_config("malformed", "[unterminated\n");
let out = run(&["--config", cfg.to_str().unwrap(), "check"]);
assert!(
!out.status.success(),
"malformed config must be a hard error"
);
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(
stderr.contains("parsing config file"),
"expected a parse error, got: {stderr}"
);
let _ = std::fs::remove_file(&cfg);
}
#[test]
fn explicit_missing_config_path_is_an_error() {
let out = run(&["--config", "/no/such/dir/freshdock.toml", "check"]);
assert!(!out.status.success(), "an explicit missing path must error");
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(
stderr.contains("reading config file"),
"expected a read error, got: {stderr}"
);
}
#[test]
fn unknown_notification_type_is_a_config_error() {
let cfg = temp_config(
"bad-notify",
"[notifications.x]\ntype = \"bogus\"\nurl = \"https://example.com\"\n",
);
let out = run(&["--config", cfg.to_str().unwrap(), "check"]);
assert!(
!out.status.success(),
"an unknown notification type must fail at parse"
);
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(
stderr.contains("parsing config file"),
"expected a parse error, got: {stderr}"
);
let _ = std::fs::remove_file(&cfg);
}