mod common;
use std::io::Write;
use std::process::{Command, Stdio};
use common::setup_polyglot_git_repo;
fn bin() -> &'static str {
env!("CARGO_BIN_EXE_callisto")
}
#[test]
fn subprocess_lifecycle_exercises_every_main_dispatch_arm() {
let dir = setup_polyglot_git_repo();
let root = dir.path();
let root_str = root.to_string_lossy().to_string();
let run = |args: &[&str]| -> std::process::Output {
Command::new(bin())
.args(args)
.output()
.unwrap_or_else(|e| panic!("failed to spawn callisto {args:?}: {e}"))
};
let init = run(&["--cwd", &root_str, "--format", "json", "init", "--yes"]);
assert!(
init.status.success(),
"init failed: {}",
String::from_utf8_lossy(&init.stderr)
);
let status = run(&["--cwd", &root_str, "--format", "json", "status"]);
assert!(
status.status.success(),
"status failed: {}",
String::from_utf8_lossy(&status.stderr)
);
let add = run(&[
"--cwd",
&root_str,
"--format",
"json",
"add",
"--package",
"core-crate:patch",
"--summary",
"Fix a bug",
]);
assert!(
add.status.success(),
"add failed: {}",
String::from_utf8_lossy(&add.stderr)
);
let validate = run(&["--cwd", &root_str, "--format", "json", "validate"]);
assert!(
validate.status.success(),
"validate failed: {}",
String::from_utf8_lossy(&validate.stderr)
);
let plan_publish = run(&["--cwd", &root_str, "--format", "json", "plan-publish"]);
assert!(
plan_publish.status.success(),
"plan-publish failed: {}",
String::from_utf8_lossy(&plan_publish.stderr)
);
let publish = run(&["--cwd", &root_str, "--format", "json", "--dry-run", "publish"]);
assert!(
publish.status.success(),
"publish --dry-run failed: {}",
String::from_utf8_lossy(&publish.stderr)
);
let compose = run(&["--cwd", &root_str, "--format", "json", "compose-pr-body"]);
assert!(
compose.status.success(),
"compose-pr-body failed: {}",
String::from_utf8_lossy(&compose.stderr)
);
let version = run(&["--cwd", &root_str, "--format", "json", "--dry-run", "version"]);
assert!(
version.status.success(),
"version --dry-run failed: {}",
String::from_utf8_lossy(&version.stderr)
);
let completions = run(&["completions", "bash"]);
assert!(
completions.status.success(),
"completions failed: {}",
String::from_utf8_lossy(&completions.stderr)
);
assert!(!completions.stdout.is_empty(), "completions must print a script");
}
#[test]
fn tag_plan_dash_reads_from_stdin() {
let dir = setup_polyglot_git_repo();
let root = dir.path();
let root_str = root.to_string_lossy().to_string();
let init = Command::new(bin())
.args(["--cwd", &root_str, "--format", "json", "init", "--yes"])
.output()
.unwrap();
assert!(init.status.success());
let plan_json = serde_json::json!({
"schemaVersion": 1,
"rustCrates": [],
"npmPlatformPackages": [],
"npmMainPackages": [],
"releases": []
})
.to_string();
let mut child = Command::new(bin())
.args([
"--cwd",
&root_str,
"--format",
"json",
"--dry-run",
"tag",
"--plan",
"-",
])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.unwrap();
child.stdin.take().unwrap().write_all(plan_json.as_bytes()).unwrap();
let output = child.wait_with_output().unwrap();
assert!(
output.status.success(),
"tag --plan - failed: {}",
String::from_utf8_lossy(&output.stderr)
);
}
#[test]
fn compose_pr_body_existing_body_dash_reads_from_stdin() {
let dir = setup_polyglot_git_repo();
let root = dir.path();
let root_str = root.to_string_lossy().to_string();
let init = Command::new(bin())
.args(["--cwd", &root_str, "--format", "json", "init", "--yes"])
.output()
.unwrap();
assert!(init.status.success());
let add = Command::new(bin())
.args([
"--cwd",
&root_str,
"--format",
"json",
"add",
"--package",
"core-crate:patch",
"--summary",
"Fix a bug",
])
.output()
.unwrap();
assert!(add.status.success());
let mut child = Command::new(bin())
.args([
"--cwd",
&root_str,
"--format",
"json",
"compose-pr-body",
"--existing-body",
"-",
])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.unwrap();
child
.stdin
.take()
.unwrap()
.write_all("\u{FEFF}Custom maintainer note.\n\n## Release Preview\n\nStale content.\n".as_bytes())
.unwrap();
let output = child.wait_with_output().unwrap();
assert!(
output.status.success(),
"compose-pr-body --existing-body - failed: {}",
String::from_utf8_lossy(&output.stderr)
);
let json: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap();
let body = json["body"].as_str().expect("body must be a string");
assert!(
body.contains("Custom maintainer note."),
"composed body must preserve the prefix above '## Release Preview' from the stdin-supplied existing body, got:\n{body}"
);
assert!(
!body.contains('\u{FEFF}'),
"leading BOM must be stripped before composing, got:\n{body}"
);
assert!(
!body.contains("Stale content."),
"content at or after '## Release Preview' in the existing body must not survive, got:\n{body}"
);
}