Skip to content

Commit 574a6c9

Browse files
committed
docs(AGENTS): add explicit preference against mocks; tests: remove mock usage in extensionManager.test.ts
- Document testing without mocks, prefer real IPC/processes and temp dirs - Remove unused bun:test mock patterns from unit tests _Generated with cmux_
1 parent 5080060 commit 574a6c9

File tree

2 files changed

+25
-21
lines changed

2 files changed

+25
-21
lines changed

docs/AGENTS.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,30 @@ await env.mockIpcRenderer.invoke(IPC_CHANNELS.WORKSPACE_CREATE, projectPath, bra
268268
- Verifying filesystem state (like checking if files exist) after IPC operations complete
269269
- Loading existing data to avoid expensive API calls in test setup
270270

271+
### Testing without Mocks (preferred)
272+
273+
- Prefer exercising real behavior over substituting test doubles. Do not stub `child_process`, `fs`, or discovery logic.
274+
- Use temporary directories and real processes in unit tests where feasible. Clean up with `fs.rmSync(temp, { recursive: true, force: true })` in `afterEach`.
275+
- For extension system tests:
276+
- Spawn the real global extension host via `ExtensionManager.initializeGlobal()`.
277+
- Create real on-disk extensions in a temp `~/.cmux/ext` or project `.cmux/ext` folder.
278+
- Register/unregister real workspaces and verify through actual tool execution.
279+
- Integration tests must go through real IPC. Use the test harness's `mockIpcRenderer.invoke()` to traverse the production IPC path (this is a façade, not a Jest mock).
280+
- Avoid spies and partial mocks. If a mock seems necessary, consider fixing the test harness or refactoring code to make the behavior testable without mocks.
281+
- Acceptable exceptions: isolating nondeterminism (e.g., time) or external network calls. Prefer dependency injection with in-memory fakes over broad module mocks.
282+
283+
### Testing without Mocks (preferred)
284+
285+
- Prefer exercising real behavior over substituting test doubles. Do not stub `child_process`, `fs`, or discovery logic.
286+
- Use temporary directories and real processes in unit tests where feasible. Clean up with `fs.rmSync(temp, { recursive: true, force: true })` in `afterEach`.
287+
- For extension system tests:
288+
- Spawn the real global extension host via `ExtensionManager.initializeGlobal()`.
289+
- Create real on-disk extensions in a temp `~/.cmux/ext` or project `.cmux/ext` folder.
290+
- Register/unregister real workspaces and verify through actual tool execution.
291+
- Integration tests must go through real IPC. Use the test harness's `mockIpcRenderer.invoke()` to traverse the production IPC path (this is a façade, not a Jest mock).
292+
- Avoid spies and partial mocks. If a mock seems necessary, consider fixing the test harness or refactoring code to make the behavior testable without mocks.
293+
- Acceptable exceptions: isolating nondeterminism (e.g., time) or external network calls. Prefer dependency injection with in-memory fakes over broad module mocks.
294+
271295
If IPC is hard to test, fix the test infrastructure or IPC layer, don't work around it by bypassing IPC.
272296

273297
## Command Palette (Cmd+Shift+P)

src/services/extensions/extensionManager.test.ts

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,12 @@
11
/* eslint-disable local/no-sync-fs-methods -- Test file uses sync fs for simplicity */
2-
/* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-explicit-any -- Mock setup requires any types */
3-
import { describe, test, beforeEach, afterEach, mock } from "bun:test";
2+
import { describe, test, beforeEach, afterEach } from "bun:test";
43
import { ExtensionManager } from "./extensionManager";
54
import type { WorkspaceMetadata } from "@/types/workspace";
65
import type { RuntimeConfig } from "@/types/runtime";
7-
import { EventEmitter } from "events";
86
import * as fs from "fs";
97
import * as path from "path";
108
import * as os from "os";
119

12-
// Mock child_process (not actually used in tests since we test the real thing)
13-
const mockChildProcess = {
14-
fork: mock((_scriptPath: string, _options?: unknown) => {
15-
const mockProcess = new EventEmitter() as any;
16-
mockProcess.send = mock(() => true);
17-
mockProcess.kill = mock(() => true);
18-
mockProcess.killed = false;
19-
mockProcess.stdout = new EventEmitter();
20-
mockProcess.stderr = new EventEmitter();
21-
return mockProcess;
22-
}),
23-
};
24-
25-
// Mock the discovery function (not actually used in tests)
26-
const mockDiscoverExtensions = mock(() => Promise.resolve([]));
2710

2811
describe("ExtensionManager", () => {
2912
let manager: ExtensionManager;
@@ -33,9 +16,6 @@ describe("ExtensionManager", () => {
3316
let runtimeConfig: RuntimeConfig;
3417

3518
beforeEach(() => {
36-
// Reset all mocks
37-
mockChildProcess.fork.mockClear();
38-
mockDiscoverExtensions.mockClear();
3919

4020
// Create temp directory for test
4121
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "ext-mgr-test-"));

0 commit comments

Comments
 (0)