mmd-anim-cli 0.3.3

CLI diagnostics and roundtrip tools for mmd-anim
use std::{
    env, fs,
    path::{Path, PathBuf},
    process::{Command, Output},
    time::{SystemTime, UNIX_EPOCH},
};

fn run_cli(args: &[&str]) -> Output {
    Command::new(env!("CARGO_BIN_EXE_mmd-anim"))
        .args(args)
        .output()
        .expect("mmd-anim CLI should run")
}

fn unique_temp_path(name: &str) -> PathBuf {
    let nanos = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .expect("system clock should be after the Unix epoch")
        .as_nanos();
    env::temp_dir().join(format!(
        "mmd-anim-cli-exit-{}-{nanos}-{name}",
        std::process::id()
    ))
}

fn stderr_text(output: &Output) -> String {
    String::from_utf8_lossy(&output.stderr).into_owned()
}

#[test]
fn success_exits_with_zero() {
    let output = run_cli(&["--version"]);

    assert_eq!(output.status.code(), Some(0), "{}", stderr_text(&output));
}

#[test]
fn clap_argument_error_exits_with_two() {
    let output = run_cli(&["inspect"]);

    assert_eq!(output.status.code(), Some(2), "{}", stderr_text(&output));
}

#[test]
fn execution_error_exits_with_one_and_reports_input_path() {
    let missing = unique_temp_path("missing.pmx");
    let output = run_cli(&["inspect", path_text(&missing)]);
    let stderr = stderr_text(&output);

    assert_eq!(output.status.code(), Some(1), "{stderr}");
    assert!(stderr.contains(path_text(&missing)), "{stderr}");
}

#[test]
fn detected_format_parse_error_reports_path_and_format() {
    let malformed = unique_temp_path("malformed.pmx");
    fs::write(&malformed, b"PMX ").expect("malformed PMX fixture should be written");

    let output = run_cli(&["inspect", path_text(&malformed)]);
    let stderr = stderr_text(&output);
    let _ = fs::remove_file(&malformed);

    assert_eq!(output.status.code(), Some(1), "{stderr}");
    assert!(stderr.contains(path_text(&malformed)), "{stderr}");
    assert!(stderr.contains("format=PMX"), "{stderr}");
    assert!(!stderr.contains("detected="), "{stderr}");
}

fn path_text(path: &Path) -> &str {
    path.to_str().expect("temporary path should be valid UTF-8")
}