prek 0.5.6

A fast Git hook manager written in Rust, designed as a drop-in alternative to pre-commit, reimagined.
mod common;

use indoc::indoc;

use crate::common::{TestEnv, cmd_snapshot};

fn config() -> &'static str {
    indoc! {r#"
        repos:
          - repo: local
            hooks:
              - id: exec-test
                name: Exec test
                entry: command-that-must-not-run
                language: system
                args: [argument-that-must-not-be-passed]
                env:
                  GIT_AUTHOR_NAME: Prek Exec
                  GIT_AUTHOR_EMAIL: exec@prek.dev
                  GIT_AUTHOR_DATE: "2000-01-01T00:00:00+00:00"
    "#}
}

fn context_with_config() -> TestEnv {
    TestEnv::new().with_config(config()).init_git()
}

#[test]
fn exec_inherits_stdin_and_stdout() {
    let context = context_with_config();

    cmd_snapshot!(context, context.exec()
        .args(["exec-test", "--", "git", "stripspace"])
        .pass_stdin("hello from prek exec\n"), @r"
    success: true
    exit_code: 0
    ----- stdout -----
    hello from prek exec

    ----- stderr -----
    ");
}

#[test]
fn exec_applies_hook_environment_without_entry_or_args() {
    let context = context_with_config();

    cmd_snapshot!(context, context.exec().args([
            "exec-test",
            "--",
            "git",
            "var",
            "GIT_AUTHOR_IDENT",
        ]), @r"
    success: true
    exit_code: 0
    ----- stdout -----
    Prek Exec <exec@prek.dev> 946684800 +0000

    ----- stderr -----
    ");
}

#[test]
fn exec_explicit_selector_ignores_skip_environment() {
    let context = context_with_config();

    cmd_snapshot!(context, context.exec()
        .env("PREK_SKIP", "exec-test")
        .args([
            "exec-test",
            "--",
            "git",
            "rev-parse",
            "--is-inside-work-tree",
        ]), @r"
    success: true
    exit_code: 0
    ----- stdout -----
    true

    ----- stderr -----
    ");
}

#[test]
fn exec_propagates_child_exit_status() {
    let context = context_with_config();

    cmd_snapshot!(context, context.exec().args([
            "exec-test",
            "--",
            "git",
            "-c",
            "alias.exit-42=!exit 42",
            "exit-42",
        ]), @r"
    success: false
    exit_code: 42
    ----- stdout -----

    ----- stderr -----
    ");
}

#[test]
fn exec_rejects_ambiguous_hook_selector() {
    let context = TestEnv::new()
        .with_workspace(["frontend"], config())
        .init_git();

    cmd_snapshot!(context, context.exec().args([
        "exec-test",
        "--",
        "git",
        "rev-parse",
        "--is-inside-work-tree",
    ]), @r"
    success: false
    exit_code: 2
    ----- stdout -----

    ----- stderr -----
    error: Hook selector `exec-test` matched multiple hooks:
      - frontend:exec-test
      - .:exec-test
    Use a `project-path:hook-id` selector to select one hook
    ");
}

#[test]
fn exec_keeps_current_working_directory() {
    let context = TestEnv::new()
        .with_workspace(["frontend"], config())
        .init_git();

    cmd_snapshot!(context, context.exec().args([
            "frontend:exec-test",
            "--",
            "git",
            "rev-parse",
            "--show-prefix",
        ]), @r"
    success: true
    exit_code: 0
    ----- stdout -----


    ----- stderr -----
    ");
}

#[cfg(unix)]
#[test]
fn exec_resolves_relative_command_from_current_working_directory() {
    let context = TestEnv::new()
        .with_workspace(["frontend"], config())
        .with_executable_file("exec-tool", "#!/bin/sh\necho relative command ok\n")
        .init_git();

    cmd_snapshot!(context, context.exec().args([
        "frontend:exec-test",
        "--",
        "./exec-tool",
    ]), @r"
    success: true
    exit_code: 0
    ----- stdout -----
    relative command ok

    ----- stderr -----
    ");
}

#[test]
fn exec_rejects_unsupported_language_before_install() {
    let context = TestEnv::new()
        .with_config(indoc::indoc! {r"
        repos:
          - repo: local
            hooks:
              - id: julia-hook
                name: Julia hook
                entry: hook.jl
                language: julia
    "})
        .init_git();

    cmd_snapshot!(context, context.exec().args([
        "julia-hook",
        "--",
        "git",
        "rev-parse",
        "--is-inside-work-tree",
    ]), @r"
    success: false
    exit_code: 2
    ----- stdout -----

    ----- stderr -----
    error: `prek exec` does not support hooks with language `julia`
    ");
}