forked from mirrors/kingfisher
351 lines
11 KiB
Rust
351 lines
11 KiB
Rust
use assert_cmd::assert::OutputAssertExt;
|
|
use assert_cmd::Command;
|
|
use predicates::str::contains;
|
|
use std::fs;
|
|
use std::path::{Path, PathBuf};
|
|
use std::process::Command as StdCommand;
|
|
use tempfile::TempDir;
|
|
|
|
fn project_root() -> PathBuf {
|
|
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
|
}
|
|
|
|
fn copy_scripts(dest: &Path) {
|
|
let scripts_dir = dest.join("scripts");
|
|
fs::create_dir_all(&scripts_dir).unwrap();
|
|
|
|
let src = project_root().join("scripts").join("install-kingfisher-pre-commit.sh");
|
|
let dst = scripts_dir.join("install-kingfisher-pre-commit.sh");
|
|
fs::copy(src, dst).unwrap();
|
|
}
|
|
|
|
fn init_repo() -> (TempDir, PathBuf, PathBuf) {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
let repo = dir.path().to_path_buf();
|
|
|
|
copy_scripts(&repo);
|
|
|
|
Command::new("git").arg("init").current_dir(&repo).assert().success();
|
|
|
|
let hooks_path = repo.join(".git/hooks");
|
|
fs::create_dir_all(&hooks_path).unwrap();
|
|
|
|
(dir, repo.clone(), hooks_path)
|
|
}
|
|
|
|
fn install(repo: &Path, hooks_path: &Path) {
|
|
Command::new("bash")
|
|
.arg(repo.join("scripts/install-kingfisher-pre-commit.sh"))
|
|
.arg("--hooks-path")
|
|
.arg(hooks_path)
|
|
.current_dir(repo)
|
|
.assert()
|
|
.success()
|
|
.stdout(contains("Kingfisher pre-commit hook installed"));
|
|
}
|
|
|
|
//
|
|
// =====================================================
|
|
// REPO-MODE TESTS (original ones, unchanged)
|
|
// =====================================================
|
|
//
|
|
|
|
#[test]
|
|
fn installs_wrapper_without_existing_hook() {
|
|
let (_tmp, repo, hooks_path) = init_repo();
|
|
|
|
install(&repo, &hooks_path);
|
|
|
|
let pre_commit = hooks_path.join("pre-commit");
|
|
let kf_wrapper = hooks_path.join("kingfisher-pre-commit");
|
|
let legacy = hooks_path.join("pre-commit.legacy.kingfisher");
|
|
|
|
let wrapper = fs::read_to_string(&pre_commit).unwrap();
|
|
let kf_script = fs::read_to_string(&kf_wrapper).unwrap();
|
|
|
|
assert!(wrapper.contains("# Kingfisher pre-commit wrapper"));
|
|
assert!(wrapper.contains("kingfisher-pre-commit"));
|
|
assert!(kf_script
|
|
.contains("kingfisher scan . --staged --quiet --redact --only-valid --no-update-check"));
|
|
assert!(!legacy.exists());
|
|
}
|
|
|
|
#[test]
|
|
fn preserves_existing_hook_and_runs_it_first() {
|
|
let (_tmp, repo, hooks_path) = init_repo();
|
|
|
|
let log = repo.join("hook.log");
|
|
let legacy = hooks_path.join("pre-commit");
|
|
fs::write(&legacy, format!("#!/usr/bin/env bash\necho legacy >> {}\n", log.display())).unwrap();
|
|
StdCommand::new("chmod").args(["+x", legacy.to_str().unwrap()]).assert().success();
|
|
|
|
let bin_dir = repo.join("bin");
|
|
fs::create_dir_all(&bin_dir).unwrap();
|
|
|
|
let fake_kingfisher = bin_dir.join("kingfisher");
|
|
fs::write(
|
|
&fake_kingfisher,
|
|
format!("#!/usr/bin/env bash\necho \"kingfisher $*\" >> {}\n", log.display()),
|
|
)
|
|
.unwrap();
|
|
StdCommand::new("chmod").args(["+x", fake_kingfisher.to_str().unwrap()]).assert().success();
|
|
|
|
install(&repo, &hooks_path);
|
|
|
|
// Execute wrapper
|
|
let wrapper = hooks_path.join("pre-commit");
|
|
StdCommand::new(wrapper)
|
|
.current_dir(&repo)
|
|
.env("PATH", format!("{}:{}", bin_dir.display(), std::env::var("PATH").unwrap()))
|
|
.assert()
|
|
.success();
|
|
|
|
let log_contents = fs::read_to_string(&log).unwrap();
|
|
let lines: Vec<_> = log_contents.lines().collect();
|
|
assert_eq!(lines[0], "legacy");
|
|
assert!(lines[1]
|
|
.contains("kingfisher scan . --staged --quiet --redact --only-valid --no-update-check"));
|
|
|
|
assert!(hooks_path.join("pre-commit.legacy.kingfisher").exists());
|
|
}
|
|
|
|
#[test]
|
|
fn uninstall_restores_original_hook() {
|
|
let (_tmp, repo, hooks_path) = init_repo();
|
|
|
|
let legacy = hooks_path.join("pre-commit");
|
|
fs::write(&legacy, "#!/usr/bin/env bash\necho legacy\n").unwrap();
|
|
StdCommand::new("chmod").args(["+x", legacy.to_str().unwrap()]).assert().success();
|
|
|
|
install(&repo, &hooks_path);
|
|
|
|
Command::new("bash")
|
|
.arg(repo.join("scripts/install-kingfisher-pre-commit.sh"))
|
|
.arg("--uninstall")
|
|
.arg("--hooks-path")
|
|
.arg(&hooks_path)
|
|
.current_dir(&repo)
|
|
.assert()
|
|
.success();
|
|
|
|
let restored = hooks_path.join("pre-commit");
|
|
let restored_content = fs::read_to_string(&restored).unwrap();
|
|
assert!(restored_content.contains("legacy"));
|
|
assert!(!restored_content.contains("Kingfisher pre-commit wrapper"));
|
|
assert!(!hooks_path.join("kingfisher-pre-commit").exists());
|
|
assert!(!hooks_path.join("pre-commit.legacy.kingfisher").exists());
|
|
}
|
|
|
|
#[test]
|
|
fn uninstall_removes_wrapper_when_no_previous_hook() {
|
|
let (_tmp, repo, hooks_path) = init_repo();
|
|
|
|
install(&repo, &hooks_path);
|
|
|
|
Command::new("bash")
|
|
.arg(repo.join("scripts/install-kingfisher-pre-commit.sh"))
|
|
.arg("--uninstall")
|
|
.arg("--hooks-path")
|
|
.arg(&hooks_path)
|
|
.current_dir(&repo)
|
|
.assert()
|
|
.success();
|
|
|
|
assert!(!hooks_path.join("pre-commit").exists());
|
|
assert!(!hooks_path.join("kingfisher-pre-commit").exists());
|
|
assert!(!hooks_path.join("pre-commit.legacy.kingfisher").exists());
|
|
}
|
|
|
|
#[test]
|
|
fn errors_outside_git_repository() {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
copy_scripts(dir.path());
|
|
|
|
Command::new("bash")
|
|
.arg(dir.path().join("scripts/install-kingfisher-pre-commit.sh"))
|
|
.current_dir(dir.path())
|
|
.assert()
|
|
.failure()
|
|
.stderr(contains("must be run inside a Git repository"));
|
|
}
|
|
|
|
#[test]
|
|
fn pre_commit_framework_invokes_kingfisher() {
|
|
let (_tmp, repo, hooks_path) = init_repo();
|
|
|
|
let log = repo.join("hook.log");
|
|
let bin_dir = repo.join("bin");
|
|
fs::create_dir_all(&bin_dir).unwrap();
|
|
|
|
let fake_kingfisher = bin_dir.join("kingfisher");
|
|
fs::write(&fake_kingfisher, format!("#!/usr/bin/env bash\necho \"$@\" > {}\n", log.display()))
|
|
.unwrap();
|
|
StdCommand::new("chmod").args(["+x", fake_kingfisher.to_str().unwrap()]).assert().success();
|
|
|
|
fs::write(
|
|
repo.join(".pre-commit-config.yaml"),
|
|
r#"repos:
|
|
- repo: local
|
|
hooks:
|
|
- id: kingfisher-local
|
|
name: kingfisher (local binary)
|
|
entry: kingfisher
|
|
language: system
|
|
args: ["scan", ".", "--staged", "--quiet", "--redact", "--only-valid", "--no-update-check"]
|
|
pass_filenames: false
|
|
always_run: true
|
|
"#,
|
|
)
|
|
.unwrap();
|
|
|
|
fs::write(repo.join("README.md"), "demo").unwrap();
|
|
|
|
StdCommand::new("uv")
|
|
.args(["run", "--no-config", "--with", "pre-commit", "pre-commit", "run", "--all-files"])
|
|
.current_dir(&repo)
|
|
.env("PATH", format!("{}:{}", bin_dir.display(), std::env::var("PATH").unwrap()))
|
|
.assert()
|
|
.success()
|
|
.stdout(contains("kingfisher (local binary)"));
|
|
|
|
let log_contents = fs::read_to_string(&log).unwrap();
|
|
assert!(log_contents.contains("scan"));
|
|
assert!(log_contents.contains("--staged"));
|
|
assert!(log_contents.contains("--quiet"));
|
|
assert!(log_contents.contains("--redact"));
|
|
}
|
|
|
|
#[test]
|
|
fn installer_hook_executes_kingfisher_command() {
|
|
let (_tmp, repo, hooks_path) = init_repo();
|
|
|
|
fs::write(repo.join("canary.txt"), "secret").unwrap();
|
|
StdCommand::new("git").args(["add", "canary.txt"]).current_dir(&repo).assert().success();
|
|
|
|
let log = repo.join("hook.log");
|
|
let bin_dir = repo.join("bin");
|
|
fs::create_dir_all(&bin_dir).unwrap();
|
|
|
|
let fake_kingfisher = bin_dir.join("kingfisher");
|
|
fs::write(
|
|
&fake_kingfisher,
|
|
format!("#!/usr/bin/env bash\necho \"kingfisher $@\" >> {}\n", log.display()),
|
|
)
|
|
.unwrap();
|
|
StdCommand::new("chmod").args(["+x", fake_kingfisher.to_str().unwrap()]).assert().success();
|
|
|
|
install(&repo, &hooks_path);
|
|
|
|
let wrapper = hooks_path.join("pre-commit");
|
|
StdCommand::new(wrapper)
|
|
.current_dir(&repo)
|
|
.env("PATH", format!("{}:{}", bin_dir.display(), std::env::var("PATH").unwrap()))
|
|
.assert()
|
|
.success();
|
|
|
|
let log_contents = fs::read_to_string(&log).unwrap();
|
|
assert!(log_contents
|
|
.contains("kingfisher scan . --staged --quiet --redact --only-valid --no-update-check"));
|
|
}
|
|
|
|
//
|
|
// =====================================================
|
|
// "GLOBAL" SEMANTICS TESTS USING --hooks-path
|
|
// (deterministic, no real global config)
|
|
// =====================================================
|
|
//
|
|
|
|
fn init_fake_global() -> (TempDir, PathBuf, PathBuf) {
|
|
let tmp = tempfile::tempdir().unwrap();
|
|
let root = tmp.path().to_path_buf();
|
|
let fake_global_hooks = root.join("fake-global-hooks");
|
|
fs::create_dir_all(&fake_global_hooks).unwrap();
|
|
|
|
copy_scripts(&root);
|
|
|
|
(tmp, root, fake_global_hooks)
|
|
}
|
|
|
|
#[test]
|
|
fn global_semantics_installs_wrapper_and_inner_hook() {
|
|
let (_tmp, root, hooks) = init_fake_global();
|
|
|
|
Command::new("bash")
|
|
.arg(root.join("scripts/install-kingfisher-pre-commit.sh"))
|
|
.arg("--hooks-path")
|
|
.arg(&hooks)
|
|
.assert()
|
|
.success();
|
|
|
|
assert!(hooks.join("pre-commit").exists());
|
|
assert!(hooks.join("kingfisher-pre-commit").exists());
|
|
}
|
|
|
|
#[test]
|
|
fn global_semantics_preserves_existing_hook_and_backup() {
|
|
let (_tmp, root, hooks) = init_fake_global();
|
|
|
|
let legacy = hooks.join("pre-commit");
|
|
fs::write(&legacy, "#!/usr/bin/env bash\necho global-legacy\n").unwrap();
|
|
StdCommand::new("chmod").args(["+x", legacy.to_str().unwrap()]).assert().success();
|
|
|
|
Command::new("bash")
|
|
.arg(root.join("scripts/install-kingfisher-pre-commit.sh"))
|
|
.arg("--hooks-path")
|
|
.arg(&hooks)
|
|
.assert()
|
|
.success();
|
|
|
|
assert!(hooks.join("pre-commit").exists());
|
|
assert!(hooks.join("pre-commit.legacy.kingfisher").exists());
|
|
}
|
|
|
|
#[test]
|
|
fn global_semantics_uninstall_restores_or_removes() {
|
|
let (_tmp, root, hooks) = init_fake_global();
|
|
|
|
// case 1: with existing legacy
|
|
let legacy = hooks.join("pre-commit");
|
|
fs::write(&legacy, "#!/usr/bin/env bash\necho global-legacy\n").unwrap();
|
|
StdCommand::new("chmod").args(["+x", legacy.to_str().unwrap()]).assert().success();
|
|
|
|
Command::new("bash")
|
|
.arg(root.join("scripts/install-kingfisher-pre-commit.sh"))
|
|
.arg("--hooks-path")
|
|
.arg(&hooks)
|
|
.assert()
|
|
.success();
|
|
|
|
Command::new("bash")
|
|
.arg(root.join("scripts/install-kingfisher-pre-commit.sh"))
|
|
.arg("--uninstall")
|
|
.arg("--hooks-path")
|
|
.arg(&hooks)
|
|
.assert()
|
|
.success();
|
|
|
|
// After uninstall with legacy, pre-commit should exist and contain legacy content
|
|
let restored = fs::read_to_string(hooks.join("pre-commit")).unwrap();
|
|
assert!(restored.contains("global-legacy"));
|
|
|
|
// case 2: no existing legacy, fresh install then uninstall
|
|
let (_tmp2, root2, hooks2) = init_fake_global();
|
|
Command::new("bash")
|
|
.arg(root2.join("scripts/install-kingfisher-pre-commit.sh"))
|
|
.arg("--hooks-path")
|
|
.arg(&hooks2)
|
|
.assert()
|
|
.success();
|
|
|
|
Command::new("bash")
|
|
.arg(root2.join("scripts/install-kingfisher-pre-commit.sh"))
|
|
.arg("--uninstall")
|
|
.arg("--hooks-path")
|
|
.arg(&hooks2)
|
|
.assert()
|
|
.success();
|
|
|
|
assert!(!hooks2.join("pre-commit").exists());
|
|
assert!(!hooks2.join("kingfisher-pre-commit").exists());
|
|
assert!(!hooks2.join("pre-commit.legacy.kingfisher").exists());
|
|
}
|