#[cfg(unix)]
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;
type TestResult<T> = Result<T, Box<dyn std::error::Error + Send + Sync>>;
#[test]
fn cli_help_uses_krr_root_command() -> TestResult<()> {
let output = command().arg("--help").output()?;
assert!(output.status.success());
let stdout = String::from_utf8(output.stdout)?;
assert!(stdout.contains("Usage: krr <COMMAND>"), "{stdout}");
assert!(!stdout.contains("Usage: kdr <COMMAND>"), "{stdout}");
Ok(())
}
#[test]
fn cli_renders_mermaid() -> TestResult<()> {
let runtime = temp_file("cli-mermaid.js");
std::fs::write(&runtime, fake_mermaid_bundle())?;
let markdown = temp_file("cli-mermaid.md");
let svg = temp_file("cli-mermaid.svg");
std::fs::write(&markdown, "```mermaid\ngraph TD; A-->B\n```\n")?;
let mermaid = command()
.args(["mermaid", "render", "--input"])
.arg(&markdown)
.arg("--output")
.arg(&svg)
.env("MERMAID_JS", &runtime)
.status()?;
assert!(mermaid.success());
assert!(std::fs::read_to_string(&svg)?.contains("<svg"));
Ok(())
}
#[test]
#[cfg(unix)]
fn cli_delegates_reference_commands_to_just() -> TestResult<()> {
let fixtures = std::env::temp_dir().join(format!("krr-cli-fixtures-{}", std::process::id()));
std::fs::create_dir_all(&fixtures)?;
let success_path = fake_just("success", 0)?;
assert!(reference_status("mermaid", "reference-update", &fixtures, &success_path)?.success());
assert!(reference_status("mermaid", "compare", &fixtures, &success_path)?.success());
assert!(reference_status("drawio", "bench", &fixtures, &success_path)?.success());
assert!(reference_status("plantuml", "bench", &fixtures, &success_path)?.success());
let failure_path = fake_just("failure", 7)?;
assert!(!reference_status("drawio", "compare", &fixtures, &failure_path)?.success());
Ok(())
}
#[test]
fn cli_plantuml_raw_fallback_logs_warning() -> TestResult<()> {
let input = temp_file("cli-plantuml.puml");
std::fs::write(&input, "@startuml\nAlice -> Bob: hello\n@enduml\n")?;
let output = command()
.args(["plantuml", "render", "--input"])
.arg(&input)
.arg("--runtime")
.arg(temp_file("missing-plantuml.jar"))
.output()?;
assert!(output.status.success());
assert!(String::from_utf8(output.stdout)?.contains("```plantuml"));
assert!(String::from_utf8(output.stderr)?.contains("plantuml-runtime-unavailable"));
Ok(())
}
#[test]
fn cli_drawio_default_runtime_reports_error_without_stub_svg() -> TestResult<()> {
let input = temp_file("cli-drawio.drawio");
let output = temp_file("cli-drawio.svg");
std::fs::write(&input, "<mxGraphModel><root /></mxGraphModel>")?;
let status = command()
.args(["drawio", "render", "--input"])
.arg(&input)
.arg("--output")
.arg(&output)
.env("DRAWIO_JS", temp_file("missing-drawio.js"))
.status()?;
assert!(!status.success());
assert!(!output.exists());
Ok(())
}
#[cfg(unix)]
fn reference_status(
kind: &str,
action: &str,
fixtures: &Path,
path: &Path,
) -> TestResult<std::process::ExitStatus> {
let mut cmd = command();
cmd.arg(kind).arg(action).arg("--fixtures").arg(fixtures);
if action == "compare" {
cmd.args(["--min-score", "12.5"]);
}
Ok(cmd.env("PATH", path).status()?)
}
fn command() -> Command {
Command::new(env!("CARGO_BIN_EXE_krr"))
}
#[cfg(unix)]
fn fake_just(name: &str, exit_code: i32) -> TestResult<PathBuf> {
let dir = std::env::temp_dir().join(format!("krr-fake-just-{name}-{}", std::process::id()));
std::fs::create_dir_all(&dir)?;
write_fake_just(&dir, exit_code)?;
Ok(dir)
}
#[cfg(unix)]
fn write_fake_just(dir: &Path, exit_code: i32) -> TestResult<()> {
use std::os::unix::fs::PermissionsExt;
let just = dir.join("just");
std::fs::write(&just, format!("#!/bin/sh\nexit {exit_code}\n"))?;
let mut permissions = std::fs::metadata(&just)?.permissions();
permissions.set_mode(0o755);
Ok(std::fs::set_permissions(&just, permissions)?)
}
fn temp_file(name: &str) -> PathBuf {
std::env::temp_dir().join(format!("krr-{name}-{}", std::process::id()))
}
fn fake_mermaid_bundle() -> &'static str {
r#"
globalThis.mermaid = {
initialize() {},
render: async (id, source) => ({
svg: `<svg xmlns="http://www.w3.org/2000/svg" id="${id}" width="20" height="10" viewBox="0 0 20 10"><text>${source}</text></svg>`
})
};
"#
}