Files
graphify/tests/test_hooks.py
T
Safi cf7ce3a450 fix #651: hook-check exits silently instead of emitting unsupported additionalContext
Codex Desktop rejects hookSpecificOutput.additionalContext on PreToolUse.
hook-check is now a no-op — graph guidance reaches the agent via AGENTS.md/skill.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-02 17:29:40 +01:00

145 lines
4.1 KiB
Python

"""Tests for hooks.py - git hook install/uninstall."""
import os
import subprocess
from pathlib import Path
import pytest
from graphify.hooks import install, uninstall, status, _HOOK_MARKER, _CHECKOUT_MARKER
def _make_git_repo(tmp_path: Path) -> Path:
subprocess.run(["git", "init", str(tmp_path)], check=True, capture_output=True)
return tmp_path
def test_install_creates_hook(tmp_path):
repo = _make_git_repo(tmp_path)
result = install(repo)
hook = repo / ".git" / "hooks" / "post-commit"
assert hook.exists()
assert _HOOK_MARKER in hook.read_text()
assert "installed" in result
def test_install_is_executable(tmp_path):
repo = _make_git_repo(tmp_path)
install(repo)
hook = repo / ".git" / "hooks" / "post-commit"
if os.name == "nt":
assert hook.read_text(encoding="utf-8").startswith("#!/bin/sh\n")
else:
assert hook.stat().st_mode & 0o111 # executable bit set
def test_install_idempotent(tmp_path):
repo = _make_git_repo(tmp_path)
install(repo)
result = install(repo)
assert "already installed" in result
# marker appears only once
hook = repo / ".git" / "hooks" / "post-commit"
assert hook.read_text().count(_HOOK_MARKER) == 1
def test_install_appends_to_existing_hook(tmp_path):
repo = _make_git_repo(tmp_path)
hook = repo / ".git" / "hooks" / "post-commit"
hook.write_text("#!/bin/bash\necho existing\n")
hook.chmod(0o755)
install(repo)
content = hook.read_text()
assert "existing" in content
assert _HOOK_MARKER in content
def test_uninstall_removes_hook(tmp_path):
repo = _make_git_repo(tmp_path)
install(repo)
result = uninstall(repo)
hook = repo / ".git" / "hooks" / "post-commit"
assert not hook.exists()
assert "removed" in result.lower()
def test_uninstall_no_hook(tmp_path):
repo = _make_git_repo(tmp_path)
result = uninstall(repo)
assert "nothing to remove" in result
def test_status_installed(tmp_path):
repo = _make_git_repo(tmp_path)
install(repo)
result = status(repo)
assert "installed" in result
def test_status_not_installed(tmp_path):
repo = _make_git_repo(tmp_path)
result = status(repo)
assert "not installed" in result
def test_no_git_repo_raises(tmp_path):
with pytest.raises(RuntimeError, match="No git repository"):
install(tmp_path / "not_a_repo")
def test_install_creates_post_checkout_hook(tmp_path):
repo = _make_git_repo(tmp_path)
install(repo)
hook = repo / ".git" / "hooks" / "post-checkout"
assert hook.exists()
assert _CHECKOUT_MARKER in hook.read_text()
def test_install_post_checkout_is_executable(tmp_path):
repo = _make_git_repo(tmp_path)
install(repo)
hook = repo / ".git" / "hooks" / "post-checkout"
if os.name == "nt":
assert hook.read_text(encoding="utf-8").startswith("#!/bin/sh\n")
else:
assert hook.stat().st_mode & 0o111
def test_uninstall_removes_post_checkout_hook(tmp_path):
repo = _make_git_repo(tmp_path)
install(repo)
uninstall(repo)
hook = repo / ".git" / "hooks" / "post-checkout"
assert not hook.exists()
def test_status_shows_both_hooks(tmp_path):
repo = _make_git_repo(tmp_path)
install(repo)
result = status(repo)
assert "post-commit" in result
assert "post-checkout" in result
assert result.count("installed") >= 2
def test_hook_skips_head_on_exe():
"""Hook script must skip shebang extraction for .exe binaries (Windows)."""
from graphify.hooks import _PYTHON_DETECT
assert "*.exe) _SHEBANG=" in _PYTHON_DETECT or '*.exe)' in _PYTHON_DETECT
def test_hook_check_no_additionalContext(tmp_path):
"""graphify hook-check must not emit additionalContext — Codex Desktop rejects it."""
import sys
out = tmp_path / "graphify-out"
out.mkdir()
(out / "graph.json").write_text("{}", encoding="utf-8")
result = subprocess.run(
[sys.executable, "-m", "graphify", "hook-check"],
cwd=tmp_path,
capture_output=True,
text=True,
)
assert result.returncode == 0
assert result.stdout == ""
assert result.stderr == ""