#![cfg(unix)]
use std::os::unix::fs::symlink;
use std::process::Command;
use tempfile::TempDir;
fn build_shim_dir_with_gh_symlink() -> (TempDir, std::path::PathBuf) {
let bin = env!("CARGO_BIN_EXE_git-prism");
let tmp = TempDir::new().unwrap();
let shim_dir = tmp.path().join("bin");
std::fs::create_dir_all(&shim_dir).unwrap();
let gh_link = shim_dir.join("gh");
symlink(bin, &gh_link).unwrap();
(tmp, shim_dir)
}
#[test]
#[cfg(unix)]
fn it_enters_shim_mode_when_invoked_as_gh() {
let (_tmp, shim_dir) = build_shim_dir_with_gh_symlink();
let real_path = std::env::var("PATH").unwrap_or_default();
let path = format!("{}:{}", shim_dir.display(), real_path);
let output = Command::new(shim_dir.join("gh"))
.args(["--version"])
.env("PATH", &path)
.output()
.unwrap();
let code = output.status.code().unwrap_or(-1);
assert!(
code == 0 || code == 127,
"gh --version via shim must exit 0 (gh present) or 127 (gh absent), got {code}.\nstdout: {}\nstderr: {}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr),
);
}
#[test]
#[cfg(unix)]
fn it_passes_through_gh_non_diff_subcommands() {
let (_tmp, shim_dir) = build_shim_dir_with_gh_symlink();
let real_path = std::env::var("PATH").unwrap_or_default();
let path = format!("{}:{}", shim_dir.display(), real_path);
let shim_output = Command::new(shim_dir.join("gh"))
.args(["--version"])
.env("CLAUDECODE", "1")
.env("PATH", &path)
.output()
.unwrap();
let real_path_no_shim = real_path
.split(':')
.filter(|p| *p != shim_dir.to_str().unwrap_or(""))
.collect::<Vec<_>>()
.join(":");
if let Some(real_gh) = find_gh(&real_path_no_shim) {
let real_output = Command::new(real_gh)
.args(["--version"])
.env("PATH", &real_path_no_shim)
.output()
.unwrap();
assert_eq!(
shim_output.status.code(),
real_output.status.code(),
"shim 'gh --version' exit code must match real gh.\nshim stderr: {}",
String::from_utf8_lossy(&shim_output.stderr),
);
}
}
fn find_gh(path: &str) -> Option<std::path::PathBuf> {
path.split(':').find_map(|dir| {
let p = std::path::Path::new(dir).join("gh");
if p.is_file() { Some(p) } else { None }
})
}
#[test]
#[cfg(unix)]
fn it_passes_through_gh_pr_diff_help_flag() {
use std::fs;
use std::os::unix::fs::PermissionsExt;
let (_tmp, shim_dir) = build_shim_dir_with_gh_symlink();
let stub_tmp = TempDir::new().unwrap();
let stub_dir = stub_tmp.path().join("bin");
fs::create_dir_all(&stub_dir).unwrap();
let stub = stub_dir.join("gh");
fs::write(&stub, b"#!/bin/sh\necho GH_HELP_SENTINEL\nexit 0\n").unwrap();
let mut p = fs::metadata(&stub).unwrap().permissions();
p.set_mode(0o755);
fs::set_permissions(&stub, p).unwrap();
let path = format!("{}:{}", stub_dir.display(), shim_dir.display());
let out = Command::new(shim_dir.join("gh"))
.args(["pr", "diff", "--help"])
.env("CLAUDECODE", "1")
.env("PATH", &path)
.output()
.unwrap();
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
stdout.contains("GH_HELP_SENTINEL"),
"gh pr diff --help must pass through to real gh, not be intercepted as a \
PR numbered '--help'. stdout: {stdout:?}, stderr: {:?}",
String::from_utf8_lossy(&out.stderr)
);
}