use assert_cmd::Command;
use predicates::prelude::*;
use serde_json::Value;
const BIN: &str = "rusty-stack";
#[test]
fn test_upgrade_help() {
Command::cargo_bin(BIN)
.unwrap()
.args(["upgrade", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("Rusty Stack"))
.stdout(predicate::str::contains("--yes"))
.stdout(predicate::str::contains("--dry-run"))
.stdout(predicate::str::contains("--binary-path"));
}
#[test]
fn test_upgrade_version() {
Command::cargo_bin(BIN)
.unwrap()
.arg("--version")
.assert()
.success()
.stdout(predicate::str::contains("rusty"));
}
#[test]
fn test_upgrade_dry_run_interactive() {
let assert = Command::cargo_bin(BIN)
.unwrap()
.args(["upgrade", "--dry-run"])
.assert();
let output = assert.get_output();
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(stdout.contains("Current version:"));
assert!(stdout.contains("Schema version:"));
let stderr = String::from_utf8_lossy(&output.stderr);
if output.status.success() {
assert!(stdout.contains("Latest release:"));
assert!(stdout.contains("Upgrade available:"));
} else {
assert!(stderr.contains("Unable to check latest release:"));
}
}
#[test]
fn test_upgrade_dry_run_non_interactive() {
let assert = Command::cargo_bin(BIN)
.unwrap()
.args(["upgrade", "--dry-run", "--yes"])
.assert();
let output = assert.get_output();
let payload = if output.status.success() {
&output.stdout
} else {
&output.stderr
};
let payload = String::from_utf8_lossy(payload);
let parsed: Value = serde_json::from_str(&payload).expect("dry-run JSON payload");
assert!(parsed.get("current_version").is_some());
assert!(parsed.get("schema_version").is_some());
assert!(parsed.get("upgrade_available").is_some());
if output.status.success() {
assert!(parsed.get("latest_version").is_some());
} else {
assert!(parsed.get("error").is_some());
}
}
#[test]
fn test_upgrade_non_interactive_produces_json_on_error() {
Command::cargo_bin(BIN)
.unwrap()
.args(["upgrade", "--yes"])
.assert()
.stderr(predicate::str::contains("\"status\":"));
}
#[test]
fn test_upgrade_interactive_error_is_human_readable() {
let output = Command::cargo_bin(BIN)
.unwrap()
.args(["upgrade"])
.output()
.unwrap();
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
let combined = format!("{stdout}{stderr}");
assert!(
!combined.contains("\"status\":"),
"interactive upgrade must stay human-readable, got JSON: {combined}"
);
let human_markers = [
"Upgrade Rusty Stack",
"Upgrade cancelled",
"Already up to date",
"Successfully upgraded",
"✗",
];
assert!(
human_markers.iter().any(|m| combined.contains(m)),
"interactive upgrade must emit a human-readable message, got: {combined}"
);
}
#[test]
fn test_upgrade_rejects_unknown_flags() {
Command::cargo_bin(BIN)
.unwrap()
.args(["upgrade", "--nonexistent-flag"])
.assert()
.failure()
.stderr(
predicate::str::contains("unexpected argument").or(predicate::str::contains("error")),
);
}