use assert_cmd::Command;
use predicates::prelude::*;
use run::engine::LanguageEngine;
fn run_binary() -> Command {
Command::new(assert_cmd::cargo::cargo_bin!("run"))
}
fn python_available() -> bool {
run::engine::PythonEngine::new().validate().is_ok()
}
#[test]
fn help_lists_workflow_subcommands() {
run_binary()
.arg("--help")
.assert()
.success()
.stdout(predicate::str::contains("Workflow commands:"))
.stdout(predicate::str::contains("run doctor"))
.stdout(predicate::str::contains("run cache --stats"))
.stdout(predicate::str::contains("run alias list"))
.stdout(predicate::str::contains("run share <file>"));
}
#[test]
fn alias_command_lists_builtin_aliases_and_rejects_mutation() {
run_binary()
.args(["alias", "list"])
.assert()
.success()
.stdout(predicate::str::contains("Alias"))
.stdout(predicate::str::contains("py"))
.stdout(predicate::str::contains("python"));
run_binary()
.args(["alias", "add", "p", "python"])
.assert()
.failure()
.stderr(predicate::str::contains(
"custom language aliases are not supported yet",
));
}
#[test]
fn snippet_lists_and_emits_template() {
run_binary()
.args(["snippet", "python", "--list"])
.assert()
.success()
.stdout(predicate::str::contains("http-server"));
run_binary()
.args(["snippet", "python", "http-server"])
.assert()
.success()
.stdout(predicate::str::contains("ThreadingHTTPServer"));
}
#[test]
fn cache_stats_and_clear_work() {
run_binary()
.args(["cache", "--stats"])
.assert()
.success()
.stdout(predicate::str::contains("entries:"));
run_binary()
.args(["cache", "--clear"])
.assert()
.success()
.stdout(predicate::str::contains("cache cleared"));
}
#[test]
fn fmt_unsupported_file_exits_two() {
let file = tempfile::NamedTempFile::new().expect("temp file");
run_binary()
.args(["fmt", file.path().to_str().expect("utf8 path")])
.assert()
.code(2)
.stderr(predicate::str::contains("No formatter available"));
}
#[test]
fn doctor_returns_documented_status_code() {
run_binary()
.arg("doctor")
.assert()
.code(predicate::in_iter([0, 1]))
.stdout(predicate::str::contains("Language"));
}
#[test]
fn json_output_wraps_execution_result() {
if !python_available() {
return;
}
let output = run_binary()
.args(["--json", "python", "-c", "print('json-ok')"])
.assert()
.success()
.get_output()
.stdout
.clone();
let value: serde_json::Value = serde_json::from_slice(&output).expect("valid json");
assert_eq!(value["language"], "python");
assert_eq!(
value["stdout"].as_str().unwrap_or("").replace("\r\n", "\n"),
"json-ok\n"
);
assert_eq!(value["exit_code"], 0);
}
#[test]
fn timeout_exits_with_124() {
if !python_available() {
return;
}
run_binary()
.args([
"--timeout",
"1",
"python",
"-c",
"import time; time.sleep(5)",
])
.assert()
.code(124)
.stderr(predicate::str::contains("Execution timed out after 1s"));
}
#[test]
fn default_timeout_is_unlimited() {
if !python_available() {
return;
}
run_binary()
.args(["python", "-c", "print('no-timeout')"])
.assert()
.success()
.stdout(predicate::str::contains("no-timeout"));
}