Testing
How the suite is built, how to run it, and the three ways a green run can lie to you.
npm run typecheck # tsc --noEmitnpm test # tsx --test, the unit suitenpm run build # tsup -> dist/Run all three before a release, in that order.
What the suite is
The tests are plain node:test files at the repository root, one per subject, driven through
tsx. A large part of the suite is not unit-shaped: a dozen files spawn a fake claude
and drive a real doStream turn through it, so the stream parser, the proxy broker, both
watchdogs, abort, respawn, account failover and the model fallback chain are exercised end to
end rather than mocked at the edge. src/turn-state.ts, src/turn-controller.ts and
src/stream-parser.ts have no test file of their own on purpose: they are the turn, so
those fake-CLI files are the oracle, and a test written against their shape would only assert
that the shape exists.
The full map from a subject to the file that covers it is the “Tests to touch when editing”
section of AGENTS.md. Consult it before changing anything; it is kept
current in the same change as the code.
Three ways a run can mislead you
- Tests live in
test/, one<name>.test.tsper area, andnpm testruns the globtest/*.test.ts. A new file runs without being registered anywhere. Fixtures are intest/fixtures/.test/integration.tsandtest/e2e-claude-session-bun.tsare manual runs against a realclaudeand are deliberately outside the glob. - The interactive transport has recorded transcripts per Claude Code release under
test/fixtures/interactive/<version>/, whichtest/interactive-golden.test.tsreplays through the real session against what the live turn returned. Record a new release withbun scripts/record-interactive-fixtures.ts(a few cheap Haiku turns on a logged-inclaude); it redacts paths, thinking text and hook details on the way. - Never pipe
npm testintogrepinside an&&chain. The pipeline exits with grep’s status, not the runner’s, so a red suite reads as green and the chain continues. Redirect and check instead:npm test > /tmp/run.log 2>&1; echo "EXIT=$?", then grep the file. - Every test file imports
test/isolate.tsfirst, so no run can touch your live data. It turns file logging and the model-catalog probe off and pointsXDG_STATE_HOME,XDG_CACHE_HOMEand the log directory at a scratch directory of its own, one per test process, removed when the process exits. Without it a run appends fixture lines to the liveplugin.log, which the next reader takes for a broken install, and records fake sessions in the live stores, because fake-CLI turns finish successfully. It coversnpm testand a single file alike, sonpx tsx --test test/<name>.test.tsneeds nothing else. A new test file starts its imports withimport "./isolate.js";test/isolation.test.tsfails the suite if one does not.
Three further traps are about load rather than correctness. A spec must never race the CLI
version probe’s 5-second deadline, because on a loaded machine it loses and the feature under
test is refused as “CLI too old”; resolve the probe up front instead. A fake CLI must not time
what surrounds a proxied call on a clock: the provider keeps a turn open only 100 ms after a
call arrives and waits only 250 ms for a call that follows a result, so tie the fake’s events
to the call itself. And the fake-CLI recovery tests in test/proxy-task.test.ts derive every
wait from the start watchdog, so under load they fail identically on master and on a released
tag: run the same file at the last known-green tag before suspecting your change.
Free and MIT-licensed, made by @khalilgharbaoui. If the plugin saves you time, you can buy its maintainer a coffee.
