Skip to content

Commit 046eeb6

Browse files
Sync public snapshot from freebuff-private
Source: CodebuffAI/freebuff-private@ad4cbae57e6aea40b470e0f40ec3c28b5841d9d1
1 parent 46956d6 commit 046eeb6

1 file changed

Lines changed: 32 additions & 1 deletion

File tree

‎docs/testing.md‎

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
- Prefer dependency injection over module mocking; define contracts in `common/src/types/contracts/`.
44
- Use `spyOn()` only for globals / legacy seams.
5-
- Avoid `mock.module()` for functions; use `@codebuff/common/testing/mock-modules.ts` helpers for constants only.
5+
- Avoid `mock.module()` for functions; use `@codebuff/common/testing/mock-modules.ts` helpers for constants only. A module mock lasts for the rest of the `bun test` process, and `mock.restore()` does not undo it (see "the FILE ORDER").
66

77
CLI hook testing note: React 19 + Bun + RTL `renderHook()` is unreliable; prefer integration tests via components for hook behavior.
88

@@ -787,6 +787,37 @@ spend it. So a test that needs the card resets it in `beforeEach`, not only
787787
`afterEach`. Otherwise its first test renders nothing whenever bun loads it after
788788
`workspace-app.dom` or `workspace-queue.dom` (2026-10-13, `sponsored-survey.dom`).
789789

790+
**`mock.module()` is the same hazard, and nothing in bun undoes it.** Measured on
791+
Bun 1.3.14 (2026-10-13):
792+
793+
- **`mock.restore()` does not restore a module mock.** It restores spies. A file
794+
that ends `afterAll(() => mock.restore())` leaves its module fakes in place for
795+
every file after it. Only `clearMockedModules()` from
796+
`@codebuff/common/testing/mock-modules.ts` puts a module back.
797+
- **The fake behaves two ways, depending on whether anything loaded the module
798+
first.** If nothing did, later importers get only the factory's exports, and
799+
any file that imports another one fails to load:
800+
`SyntaxError: Export named 'db' not found in module '.../db/index.ts'`. If an
801+
earlier file did, bun patches the factory's exports into the loaded module in
802+
place and keeps the rest. Later files, and earlier importers through their live
803+
bindings, then run silently against the fake. A two-file run usually shows the
804+
first; the full suite usually the second.
805+
- **Bun walks directories breadth-first.** Shallower directories come first;
806+
within a directory, files and subdirectories come in readdir order, which is
807+
fixed per tree on APFS and ext4 but differs between them. So
808+
`bun test leaker.test.ts victim.test.ts` runs the pair in that tree order, whatever
809+
the command line says (`--reporter=junit` lists it). A file can leak only into
810+
files bun loads after it: at the same depth, later in that order, or deeper.
811+
812+
So inject the dependency instead: a handler that takes its collaborators (the
813+
`_post.ts` + `route.ts` split), or an optional `deps` argument. To reproduce a
814+
suspected module-mock leak, run the leaking file with the victim. If bun orders
815+
the victim first, put the leaking file's `mock.module` calls in a preload beside
816+
it, so relative paths and `@/` resolve the same way. Then run the victim with
817+
web's own preloads, since `--preload` on the command line replaces bunfig's list.
818+
For the in-place case, `await import()` each module in that preload before
819+
mocking it.
820+
790821
**An eighth shape, and one where the wait is fast and the CHECK is slow: a
791822
failing matcher handed a mounted DOM node.** `expect(node).toBe(other)` and
792823
`expect(node).toBeNull()` build their failure message by pretty-printing the

0 commit comments

Comments
 (0)