refactor(coding-agent): migrated test mocks to vitest spyOn with Symbol.dispose cleanup

- Migrated test mocking from bun:test mock API to vitest spyOn pattern across 9 test files.
- Extracted mock setup logic into reusable helper functions with Symbol.dispose cleanup pattern.
- Replaced manual beforeEach/afterEach and try-finally blocks with TypeScript 5.2 using declarations.
- Removed 178 lines of boilerplate mock initialization and restoration code from test suite.
This commit is contained in:
can1357
2026-03-26 16:10:12 +01:00
parent 4ff0f309a1
commit df369dd7fd
10 changed files with 233 additions and 403 deletions
@@ -4,7 +4,11 @@ import { PythonKernel } from "@oh-my-pi/pi-coding-agent/ipy/kernel";
import { hookFetch, TempDir } from "@oh-my-pi/pi-utils";
import type { Subprocess } from "bun";
type SpawnOptions = Parameters<typeof Bun.spawn>[1];
type SpawnOptions = Bun.SpawnOptions.SpawnOptions<
Bun.SpawnOptions.Writable,
Bun.SpawnOptions.Readable,
Bun.SpawnOptions.Readable
>;
type FetchCall = { url: string; init?: RequestInit };
@@ -75,10 +79,6 @@ const createFakeProcess = (): Subprocess => {
describe("PythonKernel gateway lifecycle", () => {
const originalWebSocket = globalThis.WebSocket;
const originalSpawn = Bun.spawn;
const originalSleep = Bun.sleep;
const originalWhich = Bun.which;
const originalExecute = PythonKernel.prototype.execute;
const originalGatewayUrl = Bun.env.PI_PYTHON_GATEWAY_URL;
const originalGatewayToken = Bun.env.PI_PYTHON_GATEWAY_TOKEN;
const originalBunEnv = Bun.env.BUN_ENV;
@@ -86,6 +86,39 @@ describe("PythonKernel gateway lifecycle", () => {
let tempDir: TempDir;
let env: MockEnvironment;
const stubKernelRuntime = () => {
function mockSpawn(options: SpawnOptions & { cmd: string[] }): Subprocess;
function mockSpawn(cmd: string[], options?: SpawnOptions): Subprocess;
function mockSpawn(first: string[] | (SpawnOptions & { cmd: string[] }), second?: SpawnOptions): Subprocess {
if (Array.isArray(first)) {
env.spawnCalls.push({ cmd: first, options: second ?? {} });
} else {
const { cmd, ...options } = first;
env.spawnCalls.push({ cmd, options });
}
return createFakeProcess();
}
const spawnSpy = vi.spyOn(Bun, "spawn").mockImplementation(mockSpawn);
const sleepSpy = vi.spyOn(Bun, "sleep").mockImplementation(async () => undefined);
const whichSpy = vi.spyOn(Bun, "which").mockImplementation(() => "/usr/bin/python");
const executeSpy = vi.spyOn(PythonKernel.prototype, "execute").mockResolvedValue({
status: "ok",
cancelled: false,
timedOut: false,
stdinRequested: false,
});
return {
[Symbol.dispose]() {
spawnSpy.mockRestore();
sleepSpy.mockRestore();
whichSpy.mockRestore();
executeSpy.mockRestore();
},
};
};
beforeEach(() => {
tempDir = TempDir.createSync("@omp-python-kernel-");
env = { fetchCalls: [], spawnCalls: [] };
@@ -96,26 +129,6 @@ describe("PythonKernel gateway lifecycle", () => {
FakeWebSocket.instances = [];
globalThis.WebSocket = FakeWebSocket as unknown as typeof WebSocket;
Bun.spawn = ((cmd: string[] | string, options?: SpawnOptions) => {
const normalized = Array.isArray(cmd) ? cmd : [cmd];
env.spawnCalls.push({ cmd: normalized, options: options ?? {} });
return createFakeProcess();
}) as typeof Bun.spawn;
Bun.sleep = (async () => undefined) as typeof Bun.sleep;
Bun.which = (() => "/usr/bin/python") as typeof Bun.which;
Object.defineProperty(PythonKernel.prototype, "execute", {
value: (async () => ({
status: "ok",
cancelled: false,
timedOut: false,
stdinRequested: false,
})) as typeof PythonKernel.prototype.execute,
configurable: true,
});
});
afterEach(() => {
@@ -140,14 +153,11 @@ describe("PythonKernel gateway lifecycle", () => {
}
globalThis.WebSocket = originalWebSocket;
Bun.spawn = originalSpawn;
Bun.sleep = originalSleep;
Bun.which = originalWhich;
Object.defineProperty(PythonKernel.prototype, "execute", { value: originalExecute, configurable: true });
vi.restoreAllMocks();
});
it("starts shared gateway, interrupts, and shuts down", async () => {
using _runtime = stubKernelRuntime();
vi.spyOn(gatewayCoordinator, "acquireSharedGateway").mockResolvedValue({
url: "http://127.0.0.1:9999",
isShared: true,
@@ -177,6 +187,7 @@ describe("PythonKernel gateway lifecycle", () => {
});
it("throws when shared gateway kernel creation never succeeds", async () => {
using _runtime = stubKernelRuntime();
vi.spyOn(gatewayCoordinator, "acquireSharedGateway").mockResolvedValue({
url: "http://127.0.0.1:9999",
isShared: true,
@@ -197,6 +208,7 @@ describe("PythonKernel gateway lifecycle", () => {
});
it("does not throw when shutdown API fails", async () => {
using _runtime = stubKernelRuntime();
vi.spyOn(gatewayCoordinator, "acquireSharedGateway").mockResolvedValue({
url: "http://127.0.0.1:9999",
isShared: true,