Skip to content

Tests leak file handles into their temp projects: 24 fail with EPERM on Windows #1722

Description

@bompus

On Windows, bun run test fails 24 tests across 7 files. Every one of them fails after its assertions have passed, in fs.rmSync on the temp project:

× Resolution Module > PHP Include Resolution > resolves require_once to a file→file imports edge (#660)
  → EPERM, Permission denied: \?\C:\Users\…\Temp\codegraph-php-e2e-zmZ4mt
    ❯ __tests__/resolution.test.ts:3792  fs.rmSync(tempProject, { recursive: true, force: true });

Windows refuses to remove a directory that holds an open file; POSIX unlinks one happily, which is why CI does not see this. The tests are not wrong about the graph — they are wrong about the cleanup.

Two causes

1. The graph is never closed. __tests__/arkts-resolution.test.ts calls CodeGraph.initSync 7 times and close() zero times; frameworks-integration.test.ts closes in two JVM tests and not in the other three; resolution.test.ts's C++/PHP end-to-end tests additionally open a second DatabaseConnection for a raw edge query and never close that either. The SQLite handle is still open when rmSync runs. 14 tests.

2. A killed child still holds its handles. The four mcp-* suites do close the graph and do child.kill('SIGKILL') — then call rmSync synchronously in the same tick. A signalled process has not exited yet. 10 tests.

Measured, both arms, same file and command back to back: adding cg.close() to arkts-resolution.test.ts's ohpm main entry test makes it pass; removing that one line makes it fail with EPERM again.

Affected

Tests File Cause
7 arkts-resolution.test.ts unclosed graph
4 resolution.test.ts (C++/PHP e2e) unclosed graph + unclosed DatabaseConnection
3 frameworks-integration.test.ts (JVM FQN imports) unclosed graph
3 mcp-initialize.test.ts killed child not awaited
3 mcp-roots.test.ts killed child not awaited
3 mcp-subproject-adoption.test.ts killed child not awaited
1 mcp-daemon.test.ts killed child not awaited (fixed 50ms grace period)

Awaiting the tracked child is necessary but not sufficient for the MCP suites: with only that, a full-suite run still failed 2 of 4,240 — a different pair each time — because the server can leave a detached daemon the suite never tracks, and mcp-daemon's concurrent-launcher test deliberately leaves one alive.

Environment

Windows 11, Node 22, vitest run from a built tree (tsc + assets + ui workspace). Before: 3,975 passing / 24 failing / 234 skipped. Not reproduced on Linux — the guess is that nobody outside a Windows host ever sees it, and the failure is pure teardown, so no behaviour of the indexer is implicated.

PR follows.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions