The test suite leaks temp dirs: 89 mkdtempSync sites, 0 cleanups — 6731 dirs / 189MB in one day, some holding age keys #117
Labels
No labels
blocked
blocker:ci-red
blocker:conflict
blocker:drill-pending
blocker:unrequested
bug
claimed
documentation
enhancement
epic
merge-next
needs-triage
ready
release
scope:apply
scope:capture
scope:coolify-api
scope:fleet
scope:manifest
scope:secrets
stale
state:addressing
state:bots-reviewing
state:building
state:needs-human
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: heavy-duty/cast#117
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found during the 0.1.1 release drill. This is a test-suite hygiene bug, not a runtime defect —
castitself does not leak. Scoping it that way up front because the raw symptom looks much worse than it is.Symptom
After a day of test runs on one machine:
Oldest
12:16, newest22:08— all from a single day. Nothing reaps them, so the count is monotonic for the life of the machine.Cause
test/*.tscreates temp directories and never removes them:Not "some are missed" — there is no cleanup at all. No
afterAll, noafterEach, notry/finallyunlink, no EXIT trap.The leaked prefixes trace only to test files, which is what makes this test-only:
test/greenfield-cli.test.ts:151—cast-home-(a fakeHOMEfor a spawneddist/cli.js diff)test/secrets.test.ts:127,138—cast-home-test/release.test.ts:593—cast-inst-test/backup-cli.test.ts:24,113,117andtest/capture-cli.test.ts:37,113,124—cast-age-,cast-co-,cast-state-test/install-sh.test.ts:62—cast-install-grep -rn "cast-home\|cast-inst" src/ bin/returns nothing. The reconciler's own tempdir handling is not implicated by this finding, and I did not audit it here.Why it is worth fixing rather than ignoring
It contains key material. A sampled directory held exactly one file:
These are test-fixture age keys, not real secrets — but the suite is scattering private-key-shaped files across
/tmpat ~6700 per machine-day and leaving them world-readable-by-default for whatever/tmp's mode allows. That is a bad habit to leave in a repo whose entire job is managing an age-encrypted secret store, independent of whether these particular keys matter.It is unbounded. 189 MB/day on this host. On a long-lived CI runner or a developer box that runs the suite often, this grows until
/tmpfills. Nothing reports it.It makes drill and CI residue checks noisy. This was found while hunting for residue from a release drill: of the 6731 directories, only 261 were attributable to the drill itself and ~96% predated it. Anyone auditing what a run left behind has to first subtract a large pre-existing pile, which is exactly the kind of noise that hides a real leak.
Suggested fix
The mechanical fix is a shared helper rather than 89 individual
finallyblocks — something like atmp()that registers the path and anafterAllthat reaps the batch, so new tests get cleanup by default instead of by remembering. Vitest'sonTestFinished/afterAllboth work; the important property is that the default path cleans up, since the current state shows what happens when it is opt-in.Worth pairing with a suite-level assertion — after the run,
/tmp/cast-*should be empty — so a future test that allocates without cleaning fails loudly rather than adding to the pile. That is the same shape as theeof_guard_sweepinheavy-duty/box#112: a class check that stops the next occurrence surviving review.Not verified
Whether
src/has its own tempdir lifecycle problem. This finding is strictly abouttest/. The runtime prefixes did not appear in/tmp, which is evidence but not an audit.Refs
Found during the
release: 0.1.1drill (#116), step 6/7 residue hunt. The 0.1.1 release itself is unaffected — it touches zero runtime source (src/tree hash is byte-identical to 0.1.0).