ref:main

Test suite corrupts the developer's checkout via git upward-discovery (no isolation) #74

closed Opened by cole.christensen@gmail.com

Links

No links yet.

Summary

The test suite can silently corrupt the developer’s working checkout. Under the full concurrent run (mix test, max_cases: 20), integration/protocol tests that run git in scratch/temp directories can escape via git’s upward repository discovery and mutate this project’s real .git.

Observed damage on a real checkout (after mix test ran via the git_hooks pre-push):

  • remote.origin.url rewritten to a dead ephemeral test server (http://127.0.0.1:PORT/repo)
  • origin/main force-updated to scratch commits (“c1”/“c2”)
  • core.bare set to true (breaks all subsequent git status/operations)
  • stray commits (“first”/“track”/“second”/…) left on the checked-out branch
  • lfs.url pointed at a dead localhost endpoint

Root cause (deterministically reproduced)

A git command run in a directory that is not itself a repo walks up the filesystem until it finds a .git. When a test’s scratch dir setup races/fails under load, the next mutating git command (config, remote set-url, fetch, commit) finds the project’s .git and mutates it. Minimal repro from a scratch dir inside the project:

$ ( cd tmp/x/not-a-repo && git config core.bare true )
fatal: this operation must be run in a work tree # <- but it set core.bare on the PROJECT repo

The server-side test repos are in-memory and clients clone into temp dirs, so the corruption is purely this upward-escape; it is flaky because it only triggers when a setup step loses a race under load.

Fix

Set GIT_CEILING_DIRECTORIES to the project root in test/test_helper.exs, so git’s upward discovery stops before the project root and any stray command fails loudly in its own directory instead of corrupting the checkout. Verified: with the guard, the same repro fails not in a git directory (exit 128) and the real .git is untouched.

Adds a regression test (test/ex_git_objectstore/test_isolation_test.exs) asserting the guard is set and that git cannot discover the project repo from a scratch dir.

Out of scope (follow-up)

The guard contains the damage but does not fix the underlying flakiness: several protocol/HTTP/daemon integration tests fail under heavy concurrent load (port/timeout races). That load-sensitivity should be tracked and fixed separately.

Acceptance criteria

  • GIT_CEILING_DIRECTORIES guard in place; regression test passes.
  • Running the suite (even with failing tests) cannot alter the project repo’s config, refs, or worktree.