fix(test): prevent memory and resource leaks in test infrastructure
Pull Request Draft
Title: fix(test): prevent memory and resource leaks in test infrastructure
Summary
Fixes critical resource leaks in test infrastructure that were causing:
- Temporary directories accumulating indefinitely
- Spawned processes leaking on test failures
- OOM crashes during test runs (vitest workers consuming ~900MB each)
- Terminals closing unexpectedly (appears as "freezes" to developers)
Changes
1. Re-enable Temp Directory Cleanup
File: packages/opencode/test/fixture/fixture.ts
const result = {
[Symbol.asyncDispose]: async () => {
await options?.dispose?.(dirpath)
- // await fs.rm(dirpath, { recursive: true, force: true })
+ await fs.rm(dirpath, { recursive: true, force: true })
},
path: realpath,
extra: extra as T,
}
Impact: Temp directories created by tmpdir() fixture are now properly deleted when tests complete. Previously commented out, causing directories to accumulate in /tmp/ forever.
2. Add Process Cleanup Guarantees to LSP Tests
File: packages/opencode/test/lsp/client.test.ts
Wrapped all LSP client tests with try/finally blocks:
test("handles workspace/workspaceFolders request", async () => {
const handle = spawnFakeServer() as any
- const client = await Instance.provide({...})
- // ... test code ...
- await client.shutdown()
+ try {
+ const client = await Instance.provide({...})
+ // ... test code ...
+ await client.shutdown()
+ } finally {
+ handle.process.kill()
+ }
})
Impact: Fake LSP server processes are now always killed, even when tests throw exceptions before reaching shutdown(). Previously, failing tests would leave orphan Node.js processes consuming memory.
Motivation
While developing locally, terminals running OpenCode tests would "freeze" then close unexpectedly. Investigation revealed:
Evidence from System Logs
Jan 17 13:14 - vitest worker memory usage:
• vitest 1: 897 MB
• vitest 2: 900 MB
• vitest 3: 910 MB
• vitest 4: 905 MB
• vitest 5: 898 MB
• vitest 7: 912 MB
Total: ~5.3 GB for test workers alone!
OOM Killer Activity
Jan 17 13:14:28 lemarchy kernel: Out of memory: Killed process 1750015 (node (vitest 7))
total-vm:4985788kB, anon-rss:3646756kB, file-rss:620kB
V8 Stack Traces
V8::FatalProcessOutOfMemory
→ Node aborts (signal 6/ABRT)
→ Terminal closes
System State
- Swap: 3.9 GB / 4 GB used (95% full)
- Workers: 6-11 vitest workers spawned per run
- Impact: System becomes sluggish, eventually unresponsive
Testing
Verify Temp Directory Cleanup
# Run tests
cd packages/opencode
bun test --run
# Check temp directories (should be clean or minimal)
ls -la /tmp/ | grep opencode-test
Expected: Minimal or no opencode-test-* directories remaining.
Verify Process Cleanup
# Temporarily force a test to fail
# Edit packages/opencode/test/lsp/client.test.ts:
test("handles workspace/workspaceFolders request", async () => {
const handle = spawnFakeServer() as any
throw new Error("Force failure") // Temporary: test cleanup
})
# Run tests
bun test
# Check for orphan processes (should be none)
ps aux | grep fake-lsp-server
Expected: No fake-lsp-server processes running after test failure.
Verify Memory Stability in Watch Mode
# Monitor memory usage during watch mode
watch -n 5 'ps aux | grep "vitest\|bun test" | awk "{sum += \$6} END {print "Total RSS:", sum/1024, "MB"}'
# In another terminal, run tests in watch mode
cd packages/opencode
bun test # Let run for 10+ minutes
Expected: Memory usage remains stable (doesn't grow indefinitely).
Impact
- Developers: Test runs no longer consume increasing memory/disk
- CI/CD: Reduced OOM failures in test pipelines
- System: Stable swap usage, no system-wide slowdowns
- Experience: Tests run reliably without unexpected terminal closures
Related Issues
Fixes #9136
- #7261 - "v1.1.6 still has memory bloat - heap not released + MCP orphan processes" (similar pattern)
- #8899 / #8898 - "test: tests fail for developers with global git hooks" (same fixture file)
Checklist
- [x] Code follows project style guidelines
- [x] Tests pass locally:
bun test - [x] No new warnings or errors
- [x] Documentation updated (if applicable)
- [x] Commit messages are clear and descriptive
Notes
- This is a first-time open source contribution
- Process leaks are a common pattern — any test spawning subprocesses should use
try/finallyorafterEachhooks
Files Changed
packages/opencode/test/fixture/fixture.ts | 2 +-
packages/opencode/test/lsp/client.test.ts | 69 ++++++++++++++--------
2 files changed, 71 insertions(+), 58 deletions(-)
OpenCode Version
v1.1.24 (dev branch)
Environment
- OS: Arch Linux 6.18.3-arch1-1
- Terminal: Alacritty
- Node: v25.2.1
- RAM: 30 GB