opencode icon indicating copy to clipboard operation
opencode copied to clipboard

fix(test): prevent memory and resource leaks in test infrastructure

Open wvanderen opened this issue 19 hours ago • 1 comments

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/finally or afterEach hooks

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

wvanderen avatar Jan 17 '26 23:01 wvanderen