Skip to content

test(hooks): cover scripts/install-dev-hooks.sh (Closes #17) - #18

Merged
alex-mextner merged 2 commits into
mainfrom
test/install-dev-hooks-17
Jun 25, 2026
Merged

test(hooks): cover scripts/install-dev-hooks.sh (Closes #17)#18
alex-mextner merged 2 commits into
mainfrom
test/install-dev-hooks-17

Conversation

@alex-mextner

Copy link
Copy Markdown
Owner

Closes #17.

Adds tests/test_install_dev_hooks.py — automated tests for scripts/install-dev-hooks.sh, which PR #16 shipped with only sandbox verification (the review flagged the missing tests).

Approach

A pytest that drives the real installer against a throwaway, env-isolated git repo via subprocess. The installer logic lives in bash (dispatcher splicing, idempotent re-install, .bak preservation, --git-common-dir resolution, exec-bit repair), so the tests exercise the actual script rather than mocking the shell away — mocking would test nothing real.

Coverage (every path from #17 + more)

  • fresh install lands the tracked hook (exec bit + byte-identical);
  • aborts when the tracked source is missing / when run outside a git repo;
  • idempotency: re-run is byte-identical, including over a spliced dispatcher block (marker counted == 1, no double-splice);
  • dispatcher-block extraction: marker + || exit $? → spliced & preserved once, result is valid executable bash (bash -n + an end-to-end run); marker without the terminator → empty output, no tail splice, original kept in .bak;
  • .bak: a differing original is backed up; an existing .bak is left untouched (the true original is never lost);
  • linked-worktree resolution via --git-common-dir (hook lands in the common hooks/, never the per-worktree private dir);
  • exec-bit repair on the "already up to date" branch;
  • repo core.hooksPath set → hook still resolves to the common dir + warns;
  • installed hook fails closed when ./bin/3d is absent, invokes it when present, and blocks the commit (exit 1, gate marker on stderr) when the gate fails.

Safety (git-hook-installer isolation)

Every git call runs through _git_env(): pinned HOME / XDG_CONFIG_HOME / GIT_CONFIG_GLOBAL|SYSTEM|NOSYSTEM, GIT_CEILING_DIRECTORIES, and a scrub of every foreign GIT_* (incl. GIT_CONFIG_COUNT/KEY_*/VALUE_*). The tests never touch the developer's real ~/.gitconfig or ~/.config/git/hooks — they operate only inside pytest's tmp_path. Verified adversarially: a hostile parent core.hooksPath injected via GIT_CONFIG_* does not leak into the test repo.

3d test (ruff + pytest + mypy) green; 15 new tests pass. Reviewed via review diff --staged (claude-opus-4-8) → [ok].

🤖 Generated with Claude Code

https://claude.ai/code/session_01EQNpHiYWBHHQyMJP34BdHH

Add tests/test_install_dev_hooks.py: a pytest that drives the real installer
against a throwaway, env-isolated git repo via subprocess. The installer logic
is in bash (dispatcher-block splicing, idempotent re-install, .bak preservation,
--git-common-dir resolution, exec-bit repair), so the tests exercise the actual
script rather than mocking the shell away.

Coverage:
- fresh install lands the tracked hook (exec bit + byte-identical);
- aborts when the tracked source is missing / when run outside a git repo;
- idempotency: re-run leaves the file byte-identical, incl. a spliced dispatcher
  block (marker counted == 1, no double-splice);
- dispatcher-block extraction: marker + `|| exit $?` -> spliced & preserved once,
  the result is valid executable bash (bash -n + end-to-end run); marker without
  terminator -> empty output, no tail splice, original kept in .bak;
- .bak: a differing original is backed up; an existing .bak is left untouched;
- linked-worktree resolution via --git-common-dir (hook lands in the common
  hooks dir, never the per-worktree private one);
- exec-bit repair on the "already up to date" branch;
- repo core.hooksPath: hook still resolves to the common dir + warns;
- installed hook fails closed when ./bin/3d is absent, invokes it when present,
  and BLOCKS the commit (exit 1, gate marker on stderr) when the gate fails.

Safety: every git call runs through _git_env() — pinned HOME / XDG_CONFIG_HOME /
GIT_CONFIG_GLOBAL|SYSTEM|NOSYSTEM, GIT_CEILING_DIRECTORIES, and a scrub of every
foreign GIT_* (incl. GIT_CONFIG_COUNT/KEY_*/VALUE_*). The tests never read or
write the developer's real ~/.gitconfig or ~/.config/git/hooks; they operate only
inside pytest's tmp_path. Verified: a hostile parent core.hooksPath injected via
GIT_CONFIG_* does not leak into the test repo.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EQNpHiYWBHHQyMJP34BdHH

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 10d3c0d956

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread tests/test_install_dev_hooks.py
Address the codex review on PR #18: a set GIT_TEMPLATE_DIR (or a global
init.templateDir) would seed every `git init` with template files — including a
hooks/pre-commit. That foreign hook would make the first install create a .bak
(breaking test_reinstall_is_idempotent's "no backup" assertion) and could run
during the linked-worktree seed commit.

Two-layer fix in _git_env / the temp-repo setup:
- pop GIT_TEMPLATE_DIR from the test git env (covers the env var);
- pass `--template=` (empty) to every `git init` (covers a global init.templateDir,
  which the env-pop does not).

Verified adversarially: with a hostile GIT_TEMPLATE_DIR carrying a template
pre-commit in the parent env, the idempotency / fresh-install / linked-worktree
tests still pass — the seeding is fully neutralized.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EQNpHiYWBHHQyMJP34BdHH
@alex-mextner
alex-mextner merged commit 73d4afb into main Jun 25, 2026
8 checks passed
@alex-mextner
alex-mextner deleted the test/install-dev-hooks-17 branch June 25, 2026 13:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add automated tests for install-dev-hooks.sh

1 participant