|
2 | 2 |
|
3 | 3 | - Prefer dependency injection over module mocking; define contracts in `common/src/types/contracts/`. |
4 | 4 | - 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"). |
6 | 6 |
|
7 | 7 | CLI hook testing note: React 19 + Bun + RTL `renderHook()` is unreliable; prefer integration tests via components for hook behavior. |
8 | 8 |
|
@@ -787,6 +787,37 @@ spend it. So a test that needs the card resets it in `beforeEach`, not only |
787 | 787 | `afterEach`. Otherwise its first test renders nothing whenever bun loads it after |
788 | 788 | `workspace-app.dom` or `workspace-queue.dom` (2026-10-13, `sponsored-survey.dom`). |
789 | 789 |
|
| 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 | + |
790 | 821 | **An eighth shape, and one where the wait is fast and the CHECK is slow: a |
791 | 822 | failing matcher handed a mounted DOM node.** `expect(node).toBe(other)` and |
792 | 823 | `expect(node).toBeNull()` build their failure message by pretty-printing the |
|
0 commit comments