ADR-0021 — Tests run with Bun's built-in test runner
Tests run on Bun's built-in test runner, with no test-runner dependency and only Bun's types added, checked like the rest of the code.
- Status: Approved
- Date: 2026-11-01
- Deciders: @puncoz
- Approved by: @puncoz
- Approved on: 2026-11-01
- Supersedes: —
- Superseded by: —
- Design documents:
../architecture/system.mdx,AGENTS.md§5 and §11,README.md
Context
ADR-0007, approved 2026-11-01, makes tests part
of every change and leaves the runner to its own ADR, "before the first code change that needs
tests". Since then ADR-0011, ADR-0018 and ADR-0019 shipped code that was checked by hand with
throwaway scripts: src/lib/trajectory.ts (20 cases), src/lib/adr-status.ts (12),
src/lib/diagrams.ts and diagrams-plugin.ts (13). The owner chose (2026-11-01) to approve
ADR-0007 now and propose the runner next, comparing Bun's built-in runner with Vitest, then turn
the hand checks into tests.
Facts this decision rests on, checked on 2026-11-01:
- Bun 1.4.4 is the package manager (
bun.lock) and shipsbun test. A probe test run withbun testimportedsrc/components/html-embed.tsx, resolved its@/lib/diagramsimport throughtsconfig.jsonpaths, rendered it withreact-dom/server, and passed (3 tests, sandbox attribute present, a missing file and an empty title rejected). - Types.
tsconfig.jsonincludes**/*.ts, sobun run types:checkchecks test files. Without types forbun:test,tscfails: "Cannot find module 'bun:test' or its corresponding type declarations" (checked with a probe file, then removed). No@types/bunis installed. - Environment files. Bun loads
.envfiles on its own;.env.localholdsALGOLIA_WRITE_KEY.bun test --no-env-fileturns that off (bun test --help). - Client components (
trajectory-toc.tsx,orbit-station.tsx,adr-constellation.tsx) use hooks and the DOM. Rendering them needs a DOM implementation (happy-dom or jsdom), which is not installed. Their logic lives in the pure modules above. - Vitest is not installed.
Decision
- The runner is
bun test, built into Bun. No test-runner dependency. @types/bunis added todevDependencies, types only, so test files are type-checked bybun run types:checklike the rest of the code. The install runs after the owner's yes on this ADR (AGENTS.md§3).- Tests sit beside the code:
src/lib/diagrams.test.tsnext tosrc/lib/diagrams.ts,*.test.tsand*.test.tsx. Fixtures are invented, inline in the test file. - The command is
bun run test, apackage.jsonscript runningbun test --no-env-file, so no secret from.env.localis in a test's environment. - What is unit-tested. Pure modules, and server components rendered to a string with
react-dom/server. Client components are not rendered until an ADR adds a DOM; their logic is tested through the pure modules they call, and what needs a browser is said in each ADR's implementation notes (ADR-0007 decision 7). - The first tests replace the hand checks:
trajectory.ts,reading-time.ts,adr-status.ts,diagrams.tswithdiagrams-plugin.ts,<HtmlEmbed>, and the pure catalogue checks incatalog-plugin.ts(checkCatalogue,lineSegment,lineInfo,compareLines). Each is seen to fail once, by breaking the code, before it counts (ADR-0007 decision 3). - Done means
bun run test,bun run types:checkandbunx next buildpass, withbun run lintwhile it is blocked reported as such.AGENTS.md§5 and §11 list the command.
Options considered
| Option | Pros | Cons | Verdict |
|---|---|---|---|
A — bun test | Already installed with Bun; fast; TypeScript, JSX and the @/ alias work with no config (probed); Jest-style expect | Needs @types/bun for type checks; tests run on Bun while Next.js runs on Node, so a Node-only behaviour could pass here and fail there; DOM testing needs another package | Chosen (recommended) |
| B — Vitest | The common choice for Vite and React projects; runs on Node, like Next.js; jsdom and happy-dom integrations documented | A new dependency tree (Vite with it); a config file for the @/ alias and JSX; slower start | Rejected for now; the natural choice if component tests in a DOM are wanted later |
C — Node's built-in node:test | No dependency; runs on Node | No TypeScript paths or JSX without a loader; a thinner assert API | Rejected |
Consequences
Positive
- Tests run with what is installed, plus one types package.
- The hand checks of ADR-0011, ADR-0018 and ADR-0019 become tests that run on every change.
Negative / accepted trade-offs
- Tests run on Bun, the site on Node. Accepted: the tested code is pure TypeScript and React
rendering, which behave the same on both;
bunx next buildstill runs everything on Node. - Client components' rendering is not unit-tested. Accepted until a DOM is needed.
- One more devDependency,
@types/bun.
Content invariants
Unchanged. Tests use invented fixtures, never real private content (ADR-0007 decision 5).
Security considerations
- Secrets.
--no-env-filekeeps.env.local, and soALGOLIA_WRITE_KEY, out of the test environment (decision 4). No test reads a secret or calls a network service. - Supply chain.
@types/bunis type declarations only, from the DefinitelyTyped publisher, and is not bundled into the site. Its version is pinned bybun.lock. - Nothing leaves the system. Tests run locally; no live index, no network.
Implementation notes
- The owner's yes on the install, with this ADR's approval:
bun add -d @types/bun@1.4.4(devDependencies; the version matches Bun 1.4.4). -
package.json:"test": "bun test --no-env-file". - Found on implementation: TypeScript 7 loads no
@typespackage unlesstypesnames it, so@types/bunalone did not reachtsc. Tests get their owntsconfig.test.json(extendstsconfig.json,types: ["bun"]),tsconfig.jsonexcludes*.test.ts(x), andbun run types:checkruns both. Bun's types stay out of the site's own check, since the site runs on Node;next buildchecks only the site. - Tests for the modules in decision 6, and for ADR-0020's chip: 94 tests in 9 files beside the code. Each module was broken once (20 changes, from the arc's bow to the catalogue's public-only rule) and its tests failed every time, then the code was restored.
- ADR-0011, ADR-0018 and ADR-0019 implementation notes: the hand-checked items ticked, with the test files named.
-
bun run test,bun run types:checkandbunx next buildpass;bun run lintstays blocked (typescript-eslint and TypeScript 7).
Docs to update on implementation
-
AGENTS.md§5 (Testing row, commands table) and §11 (the checklist) -
README.md: commands -
PROJECT_AGENTS.md§6:bun run testamong the commands a consuming repository may run -
../architecture/system.mdx: where tests live -
./index.mdx: status on approval
ADR-0020 — People in ADRs are GitHub usernames, shown as profile chips
Everyone who decides or approves an ADR is written as a GitHub profile link, which the site shows as a chip with their avatar.
ADR-0022 — Diagrams show their Archify view on the page and their Mermaid in Markdown
Every diagram is one component holding a Mermaid fence: pages show its Archify view, and every Markdown surface carries the Mermaid.