stax 0.96.7

Fast stacked Git branches and PRs
Documentation
use crate::common;

#[cfg(unix)]
mod unix {
    use super::common::{OutputAssertions, TestRepo};
    use std::fs;
    use std::os::unix::fs::PermissionsExt;

    fn install_failing_pre_push_hook(repo: &TestRepo) {
        let hook = repo.path().join(".git/hooks/pre-push");
        fs::write(
            &hook,
            "#!/bin/sh\necho 'pre-push hook stdout blocked submit'\necho 'pre-push hook stderr blocked submit' >&2\nexit 1\n",
        )
        .expect("write pre-push hook");
        fs::set_permissions(&hook, fs::Permissions::from_mode(0o755)).expect("chmod pre-push hook");
    }

    fn repo_with_failing_pre_push_hook(branch_name: &str) -> (TestRepo, String) {
        let repo = TestRepo::new_with_remote();
        repo.configure_github_like_submit_remote();

        repo.run_stax(&["bc", branch_name]).assert_success();
        repo.create_file(
            &format!("{branch_name}.txt"),
            &format!("content for {branch_name}"),
        );
        repo.commit(&format!("Commit for {branch_name}"));
        let branch = repo.current_branch();

        install_failing_pre_push_hook(&repo);
        (repo, branch)
    }

    fn remote_has_branch(repo: &TestRepo, branch: &str) -> bool {
        let out = repo.git(&["ls-remote", "--heads", "origin", branch]);
        assert!(
            out.status.success(),
            "ls-remote failed: {}",
            TestRepo::stderr(&out)
        );

        let expected_ref = format!("refs/heads/{branch}");
        String::from_utf8_lossy(&out.stdout)
            .lines()
            .any(|line| line.ends_with(&expected_ref))
    }

    #[test]
    fn submit_no_verify_skips_pre_push_hook() {
        let (repo, branch) = repo_with_failing_pre_push_hook("submit-no-verify");

        repo.run_stax(&["ss", "--no-fetch", "--no-pr", "--no-prompt", "--yes"])
            .assert_failure()
            .assert_stderr_contains("pre-push hook stdout blocked submit")
            .assert_stderr_contains("pre-push hook stderr blocked submit");
        assert!(
            !remote_has_branch(&repo, &branch),
            "branch should not exist after hook-blocked push"
        );

        repo.run_stax(&[
            "ss",
            "--no-fetch",
            "--no-pr",
            "--no-prompt",
            "--yes",
            "--no-verify",
        ])
        .assert_success();
        assert!(
            remote_has_branch(&repo, &branch),
            "branch should be pushed when --no-verify bypasses the hook"
        );
    }

    #[test]
    fn branch_submit_alias_no_verify_skips_pre_push_hook() {
        let (repo, branch) = repo_with_failing_pre_push_hook("bs-no-verify");

        repo.run_stax(&["bs", "--no-fetch", "--no-pr", "--no-prompt", "--yes", "-n"])
            .assert_success();
        assert!(
            remote_has_branch(&repo, &branch),
            "bs -n should bypass the pre-push hook"
        );
    }

    #[test]
    fn upstack_submit_no_verify_skips_pre_push_hook() {
        let (repo, branch) = repo_with_failing_pre_push_hook("upstack-no-verify");

        repo.run_stax(&[
            "upstack",
            "submit",
            "--no-fetch",
            "--no-pr",
            "--no-prompt",
            "--yes",
            "--no-verify",
        ])
        .assert_success();
        assert!(
            remote_has_branch(&repo, &branch),
            "upstack submit --no-verify should bypass the pre-push hook"
        );
    }
}