| Dimension | Shared | Only in A | Only in B | Overlap |
|---|---|---|---|---|
| Sections | 0 | 12 | 13 | 0% |
| Commands | 0 | 5 | 3 | 0% |
| Section tags | 1 | 3 | 5 | 11% |
What each file covers
Sections
0 shared · 12 only in A · 13 only in B- − Testing style
- − Spawning processes
- − No timeouts
- − Use port 0 to get a random port
- − Creating temporary files
- − Strings
- − Test Organization
- − Nested/complex object equality
- − Common Imports from `harness`
- − Error Testing
- − Avoid dynamic import & require
- − Test Utilities
- + GitHub Actions Workflow Maintenance Guide
- + format.yml Workflow
- + Overview
- + Key Components
- + Updating the Workflow
- + Performance Optimizations
- + Troubleshooting
- + Testing Changes Locally
- + Test the clang-format script
- + Test with check mode (no modifications)
- + Test specific file exclusions
- + Should return nothing if exclusions work correctly
- + Important Notes
Commands
0 shared · 5 only in A · 3 only in B- − bun bd test <...test file>
- − bun bd <...cmd>
- − bun:test
- − bun bd <file>
- − bun bd test <file>
- + cargo fmt
- + bun scripts/glob-sources.ts cxx
- + cargo fmt --all
Section tags
1 shared · 3 only in A · 5 only in B- − code-style
- − testing-strategy
- − do-not
- + lint-format
- + architecture
- + git-pr
- + performance
- + agent-behaviour
- test
Line diff
oven-sh/bun · test/CLAUDE.md
@@ −1 @@
1To run tests:
2
3```sh
4bun bd test <...test file>
5```
6
7To run a command with your debug build of Bun:
8
9```sh
10bun bd <...cmd>
11```
12
13Note that compiling Bun may take up to 2.5 minutes. It is slow!
14
15**CRITICAL**: Do not use `bun test` to run tests. It will not have your changes. `bun bd test <...test file>` is the correct command, which compiles your code automatically.
16
17## Testing style
18
19Use `bun:test` with files that end in `*.test.{ts,js,jsx,tsx,mjs,cjs}`. If it's a test/js/node/test/{parallel,sequential}/\*.js without a .test.extension, use `bun bd <file>` instead of `bun bd test <file>` since those expect exit code 0 and don't use bun's test runner.
20
21- **Do not write flaky tests**. Unless explicitly asked, **never wait for time to pass in tests**. Always wait for the condition to be met instead of waiting for an arbitrary amount of time. **Never use hardcoded port numbers**. Always use `port: 0` to get a random port.
22- **Prefer concurrent tests over sequential tests**: When multiple tests in the same file spawn processes or write files, make them concurrent with `test.concurrent` or `describe.concurrent` unless it's very difficult to make them concurrent.
23
24### Spawning processes
25
26#### Spawning Bun in tests
27
28When spawning Bun processes, use `bunExe` and `bunEnv` from `harness`. This ensures the same build of Bun is used to run the test and ensures debug logging is silenced.
29
30##### Use `-e` for single-file tests
31
32```ts
33import { bunEnv, bunExe, tempDir } from "harness";
34import { test, expect } from "bun:test";
35
36test("single-file test spawns a Bun process", async () => {
37 await using proc = Bun.spawn({
38 cmd: [bunExe(), "-e", "console.log('Hello, world!')"],
39 env: bunEnv,
40 });
41
42 const [stdout, stderr, exitCode] = await Promise.all([
43 proc.stdout.text(),
44 proc.stderr.text(),
45 proc.exited,
46 ]);
47
48 expect(stderr).toBe("");
49 expect(stdout).toBe("Hello, world!\n");
50 expect(exitCode).toBe(0);
51});
52```
53
54##### When multi-file tests are required:
55
56```ts
57import { bunEnv, bunExe, tempDir } from "harness";
58import { test, expect } from "bun:test";
59
60test("multi-file test spawns a Bun process", async () => {
61 // If a test MUST use multiple files:
62 using dir = tempDir("my-test-prefix", {
63 "my.fixture.ts": `
64 import { foo } from "./foo.ts";
65 foo();
66 `,
67 "foo.ts": `
68 export function foo() {
69 console.log("Hello, world!");
70 }
71 `,
72 });
73
74 await using proc = Bun.spawn({
75 cmd: [bunExe(), "my.fixture.ts"],
76 env: bunEnv,
77 cwd: String(dir),
78 });
79
80 const [stdout, stderr, exitCode] = await Promise.all([
81
82 // ReadableStream in Bun supports:
83 // - `await stream.text()`
84 // - `await stream.json()`
85 // - `await stream.bytes()`
86 // - `await stream.blob()`
87 proc.stdout.text(),
88 proc.stderr.text(),
89
90 proc.exitCode,
91 ]);
92
93 expect(stdout).toBe("Hello, world!");
94 expect(stderr).toBe("");
95 expect(exitCode).toBe(0);
96```
97
98When a test file spawns a Bun process, we like for that file to end in `*-fixture.ts`. This is a convention that helps us identify the file as a test fixture and not a test itself.
99
100Generally, `await using` or `using` is a good idea to ensure proper resource cleanup. This works in most Bun APIs like Bun.listen, Bun.connect, Bun.spawn, Bun.serve, etc.
101
102#### Async/await in tests
103
104Prefer async/await over callbacks.
105
106When callbacks must be used and it's just a single callback, use `Promise.withResolvers` to create a promise that can be resolved or rejected from a callback.
107
108```ts
109const ws = new WebSocket("ws://localhost:8080");
110const { promise, resolve, reject } = Promise.withResolvers<void>(); // Can specify any type here for resolution value
111ws.onopen = resolve;
112ws.onclose = reject;
113await promise;
114```
115
116If it's several callbacks, it's okay to use callbacks. We aren't a stickler for this.
117
118### No timeouts
119
120**CRITICAL**: Do not set a timeout on tests. Bun already has timeouts.
121
122### Use port 0 to get a random port
123
124Most APIs in Bun support `port: 0` to get a random port. Never hardcode ports. Avoid using your own random port number function.
125
126### Creating temporary files
127
128Use `tempDirWithFiles` to create a temporary directory with files.
129
130```ts
131import { tempDir } from "harness";
132import path from "node:path";
133
134test("creates a temporary directory with files", () => {
135 using dir = tempDir("my-test-prefix", {
136 "file.txt": "Hello, world!",
137 });
138
139 expect(await Bun.file(path.join(String(dir), "file.txt")).text()).toBe(
140 "Hello, world!",
141 );
142});
143```
144
145### Strings
146
147To create a repetitive string, use `Buffer.alloc(count, fill).toString()` instead of `"A".repeat(count)`. "".repeat is very slow in debug JavaScriptCore builds.
148
149### Test Organization
150
151- Use `describe` blocks for grouping related tests
152- **Add tests to the existing test file for the code you're changing** — do not create a new file. Tests are organized by module (e.g., `/test/js/bun/`, `/test/js/node/`, `/test/js/web/`).
153- `/test/regression/issue/${issueNumber}.test.ts` is **only** for bugs that have a GitHub issue number **and** are true regressions (worked in a previous release, then broke). An issue number alone does not qualify — if it was never correct, put the test in the module's existing test file instead.
154- Integration tests are in `/test/integration/`
155
156### Nested/complex object equality
157
158Prefer usage of `.toEqual` rather than many `.toBe` assertions for nested or complex objects.
159
160<example>
161
162BAD (try to avoid doing this):
163
164```ts
165expect(result).toHaveLength(3);
166expect(result[0].optional).toBe(null);
167expect(result[1].optional).toBe("middle-value"); // CRITICAL: middle item's value must be preserved
168expect(result[2].optional).toBe(null);
169```
170
171**GOOD (always prefer this):**
172
173```ts
174expect(result).toEqual([
175 { optional: null },
176 { optional: "middle-value" }, // CRITICAL: middle item's value must be preserved
177 { optional: null },
178]);
179```
180
181</example>
182
183### Common Imports from `harness`
184
185```ts
186import {
187 bunExe, // Path to Bun executable
188 bunEnv, // Environment variables for Bun
189 tempDirWithFiles, // Create temporary test directories with files
190 tmpdirSync, // Create empty temporary directory
191 isMacOS, // Platform checks
192 isWindows,
193 isPosix,
194 gcTick, // Trigger garbage collection
195 withoutAggressiveGC, // Disable aggressive GC for performance tests
196} from "harness";
197```
198
199### Error Testing
200
201Always check exit codes and test error scenarios:
202
203```ts
204test("handles errors", async () => {
205 await using proc = Bun.spawn({
206 cmd: [bunExe(), "run", "invalid.js"],
207 env: bunEnv,
208 });
209
210 const exitCode = await proc.exited;
211 expect(exitCode).not.toBe(0);
212
213 // For synchronous errors
214 expect(() => someFunction()).toThrow("Expected error message");
215});
216```
217
218### Avoid dynamic import & require
219
220**Only** use dynamic import or require when the test is specifically testing something relataed to dynamic import or require. Otherwise, **always use module-scope import statements**.
221
222**BAD, do not do this**:
223
224```ts
225test("foo", async () => {
226 // BAD: Unnecessary usage of dynamic import.
227 const { readFile } = await import("node:fs");
228
229 expect(await readFile("ok.txt")).toBe("");
230});
231```
232
233**GOOD, do this:**
234
235```ts
236import { readFile } from "node:fs";
237test("foo", async () => {
238 expect(await readFile("ok.txt")).toBe("");
239});
240```
241
242### Test Utilities
243
244- Use `describe.each()` for parameterized tests
245- Use `toMatchSnapshot()` for snapshot testing
246- Use `beforeAll()`, `afterEach()`, `beforeEach()` for setup/teardown
247- Track resources (servers, clients) in arrays for cleanup in `afterEach()`
248
oven-sh/bun · .github/workflows/CLAUDE.md
@@ +1 @@
1# GitHub Actions Workflow Maintenance Guide
2
3This document provides guidance for maintaining the GitHub Actions workflows in this repository.
4
5## format.yml Workflow
6
7### Overview
8
9The `format.yml` workflow runs code formatters (Prettier, clang-format, and `cargo fmt`) on pull requests and pushes to main. It's optimized for speed by running all formatters in parallel.
10
11### Key Components
12
13#### 1. Clang-format Script (`scripts/run-clang-format.sh`)
14
15- **Purpose**: Formats C++ source and header files
16- **What it does**:
17 - Globs C++ files via `bun scripts/glob-sources.ts cxx`
18 - Finds all header files in `src/` and `packages/`
19 - Excludes third-party directories (libuv, napi, deps, vendor, sqlite, etc.)
20 - Requires specific clang-format version (no fallbacks)
21
22**Important exclusions**:
23
24- `src/runtime/napi/` - Node API headers (third-party)
25- `src/jsc/bindings/libuv/` - libuv headers (third-party)
26- `src/jsc/bindings/sqlite/` - SQLite headers (third-party)
27- `src/runtime/ffi/ffi-*.h` - FFI headers (generated/third-party)
28- `src/deps/` - Dependencies (third-party)
29- Files in `vendor/`, `third_party/`, `generated/` directories
30
31#### 2. Parallel Execution
32
33The workflow runs all three formatters simultaneously:
34
35- Each formatter outputs with a prefix (`[prettier]`, `[clang-format]`, `[rustfmt]`)
36- Output is streamed in real-time without blocking
37- Uses GitHub Actions groups (`::group::`) for collapsible sections
38
39#### 3. Tool Installation
40
41##### Clang-format-21
42
43- Installs ONLY `clang-format-21` package (not the entire LLVM toolchain)
44- Uses `--no-install-recommends --no-install-suggests` to skip unnecessary packages
45- Quiet installation with `-qq` and `-o=Dpkg::Use-Pty=0`
46
47##### Rustfmt
48
49- The pinned nightly is set via `RUSTUP_TOOLCHAIN` in the step `env:` (kept in sync with `channel` in `rust-toolchain.toml`); `cargo fmt --all` runs against the workspace at the repo root.
50- `RUSTUP_TOOLCHAIN` makes rustup ignore `rust-toolchain.toml` entirely, so the workflow installs only the host toolchain + `rustfmt` (`rustup toolchain install --profile minimal --component rustfmt`) rather than the file's full cross-target list.
51
52### Updating the Workflow
53
54#### To update the Rust toolchain:
55
561. Bump `channel` in `rust-toolchain.toml` (and `Dockerfile`/`bootstrap.sh` to match).
572. Bump `RUSTUP_TOOLCHAIN` in the `Format Code` step's `env:` block in `format.yml` to the same value.
583. Bump `RUSTUP_TOOLCHAIN` in the workflow-level `env:` block in `clippy.yml`, `miri.yml`, and `lolhtml.yml` to the same value.
594. `cargo fmt` formatting can change between nightlies; run `cargo fmt --all` locally on the new toolchain and include the resulting diff in the same PR.
60
61#### To update clang-format version:
62
631. Update `LLVM_VERSION_MAJOR` environment variable at the top of format.yml
642. Update the version check in `scripts/run-clang-format.sh`
65
66#### To add/remove file exclusions:
67
681. Edit the exclusion patterns in `scripts/run-clang-format.sh` (lines 34-39)
692. Test locally to ensure the right files are being formatted
70
71### Performance Optimizations
72
731. **Parallel execution**: All formatters run simultaneously
742. **Minimal installations**: Only required packages, no extras
753. **Streaming output**: Real-time feedback without buffering
764. **Early start**: Formatting begins immediately after each tool is ready
77
78### Troubleshooting
79
80**If formatters appear to run sequentially:**
81
82- Check if output is being buffered (should use `sed` for line prefixing)
83- Ensure background processes use `&` and proper wait commands
84
85**If third-party files are being formatted:**
86
87- Review exclusion patterns in `scripts/run-clang-format.sh`
88- Check if new third-party directories were added that need exclusion
89
90**If clang-format installation is slow:**
91
92- Ensure using minimal package installation flags
93- Check if apt cache needs updating
94- Consider caching the clang-format binary between runs
95
96### Testing Changes Locally
97
98```bash
99# Test the clang-format script
100export LLVM_VERSION_MAJOR=19
101./scripts/run-clang-format.sh format
102
103# Test with check mode (no modifications)
104./scripts/run-clang-format.sh check
105
106# Test specific file exclusions
107./scripts/run-clang-format.sh format 2>&1 | grep -E "(libuv|napi|deps)"
108# Should return nothing if exclusions work correctly
109```
110
111### Important Notes
112
113- The script defaults to **format** mode (modifies files)
114- Always test locally before pushing workflow changes
115- Keep the exclusion list updated as new third-party code is added
116
@@ −1 +1 @@
1−To run tests:
1+# GitHub Actions Workflow Maintenance Guide
22
3−```sh
4−bun bd test <...test file>
5−```
3+This document provides guidance for maintaining the GitHub Actions workflows in this repository.
64
7−To run a command with your debug build of Bun:
5+## format.yml Workflow
86
9−```sh
10−bun bd <...cmd>
11−```
7+### Overview
128
13−Note that compiling Bun may take up to 2.5 minutes. It is slow!
9+The `format.yml` workflow runs code formatters (Prettier, clang-format, and `cargo fmt`) on pull requests and pushes to main. It's optimized for speed by running all formatters in parallel.
1410
15−**CRITICAL**: Do not use `bun test` to run tests. It will not have your changes. `bun bd test <...test file>` is the correct command, which compiles your code automatically.
11+### Key Components
1612
17−## Testing style
13+#### 1. Clang-format Script (`scripts/run-clang-format.sh`)
1814
19−Use `bun:test` with files that end in `*.test.{ts,js,jsx,tsx,mjs,cjs}`. If it's a test/js/node/test/{parallel,sequential}/\*.js without a .test.extension, use `bun bd <file>` instead of `bun bd test <file>` since those expect exit code 0 and don't use bun's test runner.
15+- **Purpose**: Formats C++ source and header files
16+- **What it does**:
17+ - Globs C++ files via `bun scripts/glob-sources.ts cxx`
18+ - Finds all header files in `src/` and `packages/`
19+ - Excludes third-party directories (libuv, napi, deps, vendor, sqlite, etc.)
20+ - Requires specific clang-format version (no fallbacks)
2021
21−- **Do not write flaky tests**. Unless explicitly asked, **never wait for time to pass in tests**. Always wait for the condition to be met instead of waiting for an arbitrary amount of time. **Never use hardcoded port numbers**. Always use `port: 0` to get a random port.
22−- **Prefer concurrent tests over sequential tests**: When multiple tests in the same file spawn processes or write files, make them concurrent with `test.concurrent` or `describe.concurrent` unless it's very difficult to make them concurrent.
22+**Important exclusions**:
2323
24−### Spawning processes
24+- `src/runtime/napi/` - Node API headers (third-party)
25+- `src/jsc/bindings/libuv/` - libuv headers (third-party)
26+- `src/jsc/bindings/sqlite/` - SQLite headers (third-party)
27+- `src/runtime/ffi/ffi-*.h` - FFI headers (generated/third-party)
28+- `src/deps/` - Dependencies (third-party)
29+- Files in `vendor/`, `third_party/`, `generated/` directories
2530
26−#### Spawning Bun in tests
31+#### 2. Parallel Execution
2732
28−When spawning Bun processes, use `bunExe` and `bunEnv` from `harness`. This ensures the same build of Bun is used to run the test and ensures debug logging is silenced.
33+The workflow runs all three formatters simultaneously:
2934
30−##### Use `-e` for single-file tests
35+- Each formatter outputs with a prefix (`[prettier]`, `[clang-format]`, `[rustfmt]`)
36+- Output is streamed in real-time without blocking
37+- Uses GitHub Actions groups (`::group::`) for collapsible sections
3138
32−```ts
33−import { bunEnv, bunExe, tempDir } from "harness";
34−import { test, expect } from "bun:test";
39+#### 3. Tool Installation
3540
36−test("single-file test spawns a Bun process", async () => {
37− await using proc = Bun.spawn({
38− cmd: [bunExe(), "-e", "console.log('Hello, world!')"],
39− env: bunEnv,
40− });
41+##### Clang-format-21
4142
42− const [stdout, stderr, exitCode] = await Promise.all([
43− proc.stdout.text(),
44− proc.stderr.text(),
45− proc.exited,
46− ]);
43+- Installs ONLY `clang-format-21` package (not the entire LLVM toolchain)
44+- Uses `--no-install-recommends --no-install-suggests` to skip unnecessary packages
45+- Quiet installation with `-qq` and `-o=Dpkg::Use-Pty=0`
4746
48− expect(stderr).toBe("");
49− expect(stdout).toBe("Hello, world!\n");
50− expect(exitCode).toBe(0);
51−});
52−```
47+##### Rustfmt
5348
54−##### When multi-file tests are required:
49+- The pinned nightly is set via `RUSTUP_TOOLCHAIN` in the step `env:` (kept in sync with `channel` in `rust-toolchain.toml`); `cargo fmt --all` runs against the workspace at the repo root.
50+- `RUSTUP_TOOLCHAIN` makes rustup ignore `rust-toolchain.toml` entirely, so the workflow installs only the host toolchain + `rustfmt` (`rustup toolchain install --profile minimal --component rustfmt`) rather than the file's full cross-target list.
5551
56−```ts
57−import { bunEnv, bunExe, tempDir } from "harness";
58−import { test, expect } from "bun:test";
52+### Updating the Workflow
5953
60−test("multi-file test spawns a Bun process", async () => {
61− // If a test MUST use multiple files:
62− using dir = tempDir("my-test-prefix", {
63− "my.fixture.ts": `
64− import { foo } from "./foo.ts";
65− foo();
66− `,
67− "foo.ts": `
68− export function foo() {
69− console.log("Hello, world!");
70− }
71− `,
72− });
54+#### To update the Rust toolchain:
7355
74− await using proc = Bun.spawn({
75− cmd: [bunExe(), "my.fixture.ts"],
76− env: bunEnv,
77− cwd: String(dir),
78− });
56+1. Bump `channel` in `rust-toolchain.toml` (and `Dockerfile`/`bootstrap.sh` to match).
57+2. Bump `RUSTUP_TOOLCHAIN` in the `Format Code` step's `env:` block in `format.yml` to the same value.
58+3. Bump `RUSTUP_TOOLCHAIN` in the workflow-level `env:` block in `clippy.yml`, `miri.yml`, and `lolhtml.yml` to the same value.
59+4. `cargo fmt` formatting can change between nightlies; run `cargo fmt --all` locally on the new toolchain and include the resulting diff in the same PR.
7960
80− const [stdout, stderr, exitCode] = await Promise.all([
61+#### To update clang-format version:
8162
82− // ReadableStream in Bun supports:
83− // - `await stream.text()`
84− // - `await stream.json()`
85− // - `await stream.bytes()`
86− // - `await stream.blob()`
87− proc.stdout.text(),
88− proc.stderr.text(),
63+1. Update `LLVM_VERSION_MAJOR` environment variable at the top of format.yml
64+2. Update the version check in `scripts/run-clang-format.sh`
8965
90− proc.exitCode,
91− ]);
66+#### To add/remove file exclusions:
9267
93− expect(stdout).toBe("Hello, world!");
94− expect(stderr).toBe("");
95− expect(exitCode).toBe(0);
96−```
68+1. Edit the exclusion patterns in `scripts/run-clang-format.sh` (lines 34-39)
69+2. Test locally to ensure the right files are being formatted
9770
98−When a test file spawns a Bun process, we like for that file to end in `*-fixture.ts`. This is a convention that helps us identify the file as a test fixture and not a test itself.
71+### Performance Optimizations
9972
100−Generally, `await using` or `using` is a good idea to ensure proper resource cleanup. This works in most Bun APIs like Bun.listen, Bun.connect, Bun.spawn, Bun.serve, etc.
73+1. **Parallel execution**: All formatters run simultaneously
74+2. **Minimal installations**: Only required packages, no extras
75+3. **Streaming output**: Real-time feedback without buffering
76+4. **Early start**: Formatting begins immediately after each tool is ready
10177
102−#### Async/await in tests
78+### Troubleshooting
10379
104−Prefer async/await over callbacks.
80+**If formatters appear to run sequentially:**
10581
106−When callbacks must be used and it's just a single callback, use `Promise.withResolvers` to create a promise that can be resolved or rejected from a callback.
82+- Check if output is being buffered (should use `sed` for line prefixing)
83+- Ensure background processes use `&` and proper wait commands
10784
108−```ts
109−const ws = new WebSocket("ws://localhost:8080");
110−const { promise, resolve, reject } = Promise.withResolvers<void>(); // Can specify any type here for resolution value
111−ws.onopen = resolve;
112−ws.onclose = reject;
113−await promise;
114−```
85+**If third-party files are being formatted:**
11586
116−If it's several callbacks, it's okay to use callbacks. We aren't a stickler for this.
87+- Review exclusion patterns in `scripts/run-clang-format.sh`
88+- Check if new third-party directories were added that need exclusion
11789
118−### No timeouts
90+**If clang-format installation is slow:**
11991
120−**CRITICAL**: Do not set a timeout on tests. Bun already has timeouts.
92+- Ensure using minimal package installation flags
93+- Check if apt cache needs updating
94+- Consider caching the clang-format binary between runs
12195
122−### Use port 0 to get a random port
96+### Testing Changes Locally
12397
124−Most APIs in Bun support `port: 0` to get a random port. Never hardcode ports. Avoid using your own random port number function.
98+```bash
99+# Test the clang-format script
100+export LLVM_VERSION_MAJOR=19
101+./scripts/run-clang-format.sh format
125102
126−### Creating temporary files
103+# Test with check mode (no modifications)
104+./scripts/run-clang-format.sh check
127105
128−Use `tempDirWithFiles` to create a temporary directory with files.
129−
130−```ts
131−import { tempDir } from "harness";
132−import path from "node:path";
133−
134−test("creates a temporary directory with files", () => {
135− using dir = tempDir("my-test-prefix", {
136− "file.txt": "Hello, world!",
137− });
138−
139− expect(await Bun.file(path.join(String(dir), "file.txt")).text()).toBe(
140− "Hello, world!",
141− );
142−});
106+# Test specific file exclusions
107+./scripts/run-clang-format.sh format 2>&1 | grep -E "(libuv|napi|deps)"
108+# Should return nothing if exclusions work correctly
143109 ```
144110
145−### Strings
111+### Important Notes
146112
147−To create a repetitive string, use `Buffer.alloc(count, fill).toString()` instead of `"A".repeat(count)`. "".repeat is very slow in debug JavaScriptCore builds.
148−
149−### Test Organization
150−
151−- Use `describe` blocks for grouping related tests
152−- **Add tests to the existing test file for the code you're changing** — do not create a new file. Tests are organized by module (e.g., `/test/js/bun/`, `/test/js/node/`, `/test/js/web/`).
153−- `/test/regression/issue/${issueNumber}.test.ts` is **only** for bugs that have a GitHub issue number **and** are true regressions (worked in a previous release, then broke). An issue number alone does not qualify — if it was never correct, put the test in the module's existing test file instead.
154−- Integration tests are in `/test/integration/`
155−
156−### Nested/complex object equality
157−
158−Prefer usage of `.toEqual` rather than many `.toBe` assertions for nested or complex objects.
159−
160−<example>
161−
162−BAD (try to avoid doing this):
163−
164−```ts
165−expect(result).toHaveLength(3);
166−expect(result[0].optional).toBe(null);
167−expect(result[1].optional).toBe("middle-value"); // CRITICAL: middle item's value must be preserved
168−expect(result[2].optional).toBe(null);
169−```
170−
171−**GOOD (always prefer this):**
172−
173−```ts
174−expect(result).toEqual([
175− { optional: null },
176− { optional: "middle-value" }, // CRITICAL: middle item's value must be preserved
177− { optional: null },
178−]);
179−```
180−
181−</example>
182−
183−### Common Imports from `harness`
184−
185−```ts
186−import {
187− bunExe, // Path to Bun executable
188− bunEnv, // Environment variables for Bun
189− tempDirWithFiles, // Create temporary test directories with files
190− tmpdirSync, // Create empty temporary directory
191− isMacOS, // Platform checks
192− isWindows,
193− isPosix,
194− gcTick, // Trigger garbage collection
195− withoutAggressiveGC, // Disable aggressive GC for performance tests
196−} from "harness";
197−```
198−
199−### Error Testing
200−
201−Always check exit codes and test error scenarios:
202−
203−```ts
204−test("handles errors", async () => {
205− await using proc = Bun.spawn({
206− cmd: [bunExe(), "run", "invalid.js"],
207− env: bunEnv,
208− });
209−
210− const exitCode = await proc.exited;
211− expect(exitCode).not.toBe(0);
212−
213− // For synchronous errors
214− expect(() => someFunction()).toThrow("Expected error message");
215−});
216−```
217−
218−### Avoid dynamic import & require
219−
220−**Only** use dynamic import or require when the test is specifically testing something relataed to dynamic import or require. Otherwise, **always use module-scope import statements**.
221−
222−**BAD, do not do this**:
223−
224−```ts
225−test("foo", async () => {
226− // BAD: Unnecessary usage of dynamic import.
227− const { readFile } = await import("node:fs");
228−
229− expect(await readFile("ok.txt")).toBe("");
230−});
231−```
232−
233−**GOOD, do this:**
234−
235−```ts
236−import { readFile } from "node:fs";
237−test("foo", async () => {
238− expect(await readFile("ok.txt")).toBe("");
239−});
240−```
241−
242−### Test Utilities
243−
244−- Use `describe.each()` for parameterized tests
245−- Use `toMatchSnapshot()` for snapshot testing
246−- Use `beforeAll()`, `afterEach()`, `beforeEach()` for setup/teardown
247−- Track resources (servers, clients) in arrays for cleanup in `afterEach()`
113+- The script defaults to **format** mode (modifies files)
114+- Always test locally before pushing workflow changes
115+- Keep the exclusion list updated as new third-party code is added
248116
