simit 0.17.4

Semver-aware git commit helper for Rust projects
Documentation
use std::fs;
use std::path::Path;
use std::process::Command;

use serde_json::Value;
use tempfile::TempDir;

mod common;

fn simit() -> Command {
    common::simit()
}

fn init_fixture() -> TempDir {
    let temp = TempDir::new().unwrap();
    let root = temp.path();
    fs::write(
        root.join("Cargo.toml"),
        r#"[package]
name = "release-verify-demo"
version = "0.1.0"
edition = "2024"
publish = false
"#,
    )
    .unwrap();
    fs::create_dir(root.join("src")).unwrap();
    fs::write(root.join("src/lib.rs"), "pub fn demo() {}\n").unwrap();
    fs::write(
        root.join("CHANGELOG.md"),
        "# Changelog\n\n## [0.1.0] - 2026-05-25\n\n- Initial release.\n",
    )
    .unwrap();
    fs::create_dir_all(root.join("keys")).unwrap();
    fs::copy(
        common::maintainer_key_path(),
        root.join("keys/maintainers.gpg"),
    )
    .unwrap();

    git(root, &["init"]);
    git(root, &["add", "."]);
    git(
        root,
        &[
            "-c",
            "user.name=Test User",
            "-c",
            "user.email=test@example.com",
            "commit",
            "-m",
            "initial",
        ],
    );
    git(root, &["-c", "tag.gpgSign=false", "tag", "0.1.0"]);

    temp
}

fn git(root: &Path, args: &[&str]) {
    let status = Command::new("git")
        .args(args)
        .current_dir(root)
        .status()
        .unwrap();
    assert!(status.success(), "git {args:?} failed");
}

#[test]
fn release_verify_help_lists_verify_flags() {
    let output = simit()
        .args(["release", "verify", "--help"])
        .output()
        .unwrap();
    assert!(output.status.success());
    let stdout = String::from_utf8(output.stdout).unwrap();
    assert!(stdout.contains("--json"));
    assert!(stdout.contains("--version <VERSION>"));
    assert!(stdout.contains("--push-target <REMOTE>"));
}

#[test]
fn release_verify_json_reports_all_checks_and_exit_code() {
    let temp = init_fixture();
    fs::write(temp.path().join("dirty.txt"), "dirty\n").unwrap();

    let output = simit()
        .current_dir(temp.path())
        .args(["release", "verify", "--json"])
        .output()
        .unwrap();

    assert_eq!(output.status.code(), Some(1));
    let value: Value = serde_json::from_slice(&output.stdout).unwrap();
    assert_eq!(value["command"], "simit release verify");
    assert_eq!(value["summary"]["exit_code"], 1);

    let results = value["results"].as_array().unwrap();
    assert!(
        results
            .iter()
            .any(|item| item["check"] == "worktree clean" && item["status"] == "fail")
    );
    assert!(
        results
            .iter()
            .any(|item| item["check"] == "remote secrets" && item["status"] == "blocked")
    );
}

#[test]
fn release_verify_text_reports_missing_changelog_entry() {
    let temp = init_fixture();

    let output = simit()
        .current_dir(temp.path())
        .args(["release", "verify", "--version", "0.2.0"])
        .output()
        .unwrap();

    assert_eq!(output.status.code(), Some(1));
    let stdout = String::from_utf8(output.stdout).unwrap();
    assert!(stdout.contains("[fail"));
    assert!(stdout.contains("CHANGELOG entry exists: no released entry for 0.2.0"));
    assert!(stdout.contains("summary:"));
}