use std::process::Command;
fn devboy_bin() -> std::path::PathBuf {
let mut path = std::env::current_exe().unwrap();
path.pop(); path.pop();
let bin_name = format!("devboy{}", std::env::consts::EXE_SUFFIX);
path.push(bin_name);
path
}
fn run_upgrade_check() -> std::process::Output {
let mut cmd = Command::new(devboy_bin());
cmd.args(["upgrade", "--check"]);
if let Ok(token) = std::env::var("GITHUB_TOKEN") {
cmd.env("GITHUB_TOKEN", token);
} else if let Ok(token) = std::env::var("GH_TOKEN") {
cmd.env("GH_TOKEN", token);
}
cmd.output().expect("Failed to execute command")
}
fn is_api_unavailable(output: &std::process::Output) -> bool {
if output.status.success() {
return false;
}
let combined = format!(
"{}{}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
);
combined.contains("rate limit")
|| combined.contains("GitHub API returned status")
|| combined.contains("Failed to fetch release info")
}
#[test]
fn test_upgrade_help() {
let output = Command::new(devboy_bin())
.args(["upgrade", "--help"])
.output()
.expect("Failed to execute command");
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(output.status.success());
assert!(stdout.contains("Upgrade devboy to the latest version"));
assert!(stdout.contains("--check"));
}
#[test]
fn test_upgrade_check_shows_current_version() {
let output = run_upgrade_check();
if is_api_unavailable(&output) {
eprintln!("Skipping: GitHub API unavailable");
return;
}
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
output.status.success(),
"Command failed.\nstdout: {}\nstderr: {}",
stdout,
String::from_utf8_lossy(&output.stderr)
);
assert!(
stdout.contains("Current version:"),
"Expected 'Current version:' in output, got: {}",
stdout
);
}
#[test]
fn test_upgrade_check_outputs_version_info() {
let output = run_upgrade_check();
if is_api_unavailable(&output) {
eprintln!("Skipping: GitHub API unavailable");
return;
}
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
output.status.success(),
"Command failed.\nstdout: {}\nstderr: {}",
stdout,
String::from_utf8_lossy(&output.stderr)
);
assert!(
stdout.contains("latest version") || stdout.contains("New version available"),
"Expected version status in output, got: {}",
stdout
);
}
#[test]
fn test_upgrade_detects_npm_install_when_node_modules_in_path() {
let mut cmd = Command::new(devboy_bin());
cmd.args(["upgrade", "--check"])
.env("npm_config_user_agent", "pnpm/9.0.0 node/22.0.0");
if let Ok(token) = std::env::var("GITHUB_TOKEN") {
cmd.env("GITHUB_TOKEN", token);
} else if let Ok(token) = std::env::var("GH_TOKEN") {
cmd.env("GH_TOKEN", token);
}
let output = cmd.output().expect("Failed to execute command");
if is_api_unavailable(&output) {
eprintln!("Skipping: GitHub API unavailable");
return;
}
assert!(
output.status.success(),
"Command failed: {}",
String::from_utf8_lossy(&output.stderr)
);
}
#[test]
fn test_upgrade_appears_in_main_help() {
let output = Command::new(devboy_bin())
.args(["--help"])
.output()
.expect("Failed to execute command");
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(output.status.success());
assert!(
stdout.contains("upgrade"),
"Expected 'upgrade' in main help output"
);
}
#[test]
fn test_update_check_suppressed_in_ci() {
let output = Command::new(devboy_bin())
.args(["config", "path"])
.env("CI", "true")
.env("DEVBOY_SKIP_KEYCHAIN", "1")
.output()
.expect("Failed to execute command");
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(output.status.success());
assert!(
!stderr.contains("new version"),
"Update check should be suppressed in CI, but got stderr: {}",
stderr
);
}
#[test]
fn test_update_check_suppressed_with_env_var() {
let output = Command::new(devboy_bin())
.args(["config", "path"])
.env("DEVBOY_NO_UPDATE_CHECK", "1")
.env("DEVBOY_SKIP_KEYCHAIN", "1")
.output()
.expect("Failed to execute command");
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(output.status.success());
assert!(
!stderr.contains("new version"),
"Update check should be suppressed with DEVBOY_NO_UPDATE_CHECK=1, but got stderr: {}",
stderr
);
}
#[test]
fn test_version_flag_still_works() {
let output = Command::new(devboy_bin())
.args(["--version"])
.output()
.expect("Failed to execute command");
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(output.status.success());
assert!(
stdout.contains("devboy"),
"Expected 'devboy' in version output, got: {}",
stdout
);
}