import { afterEach, describe, expect, it } from "bun:test"; import { executePython } from "@oh-my-pi/pi-coding-agent/eval/py/executor"; import type { KernelExecuteOptions, KernelExecuteResult } from "@oh-my-pi/pi-coding-agent/eval/py/kernel"; import { PythonKernel } from "@oh-my-pi/pi-coding-agent/eval/py/kernel"; import { TempDir } from "@oh-my-pi/pi-utils"; interface KernelStub { execute: (code: string, options?: KernelExecuteOptions) => Promise; shutdown: () => Promise; } type KernelStartOptions = Parameters[0]; const originalDateNow = Date.now; const originalStart = PythonKernel.start; function createCancellationError(name: "AbortError" | "TimeoutError", message: string): Error { const error = new Error(message); error.name = name; return error; } function rejectOnStartupCancellation(options: KernelStartOptions): Promise { const { promise, reject } = Promise.withResolvers(); let settled = false; let timeout: NodeJS.Timeout | undefined; const finish = (error: unknown) => { if (settled) return; settled = true; if (timeout) clearTimeout(timeout); options.signal?.removeEventListener("abort", onAbort); reject(error); }; const onAbort = () => { finish(options.signal?.reason ?? createCancellationError("AbortError", "Python kernel startup aborted")); }; options.signal?.addEventListener("abort", onAbort, { once: true }); if (options.deadlineMs !== undefined) { const remainingMs = Math.max(0, options.deadlineMs - Date.now()); timeout = setTimeout(() => { finish(createCancellationError("TimeoutError", "Python kernel startup timed out")); }, remainingMs); timeout.unref(); } return promise; } describe("executePython (per-call)", () => { afterEach(() => { PythonKernel.start = originalStart; Date.now = originalDateNow; }); it("returns a cancelled timeout result when kernel startup exceeds the deadline", async () => { Bun.env.PI_PYTHON_SKIP_CHECK = "1"; using tempDir = TempDir.createSync("@omp-python-executor-per-call-"); PythonKernel.start = async options => await rejectOnStartupCancellation(options); const result = await executePython("sleep(10)", { kernelMode: "per-call", timeoutMs: 25, cwd: tempDir.path(), }); expect(result.cancelled).toBe(true); expect(result.exitCode).toBeUndefined(); expect(result.output).toContain("Command timed out"); }); it("returns a cancelled timeout result when the startup budget expires before kernel creation", async () => { Bun.env.PI_PYTHON_SKIP_CHECK = "1"; using tempDir = TempDir.createSync("@omp-python-executor-per-call-"); let nowCalls = 0; Date.now = () => { nowCalls += 1; return nowCalls <= 2 ? 1_000 : 2_000; }; const result = await executePython("sleep(10)", { kernelMode: "per-call", timeoutMs: 10, cwd: tempDir.path(), }); expect(result.cancelled).toBe(true); expect(result.exitCode).toBeUndefined(); expect(result.output).toContain("Command timed out"); }); it("returns a cancelled result when caller aborts during kernel startup", async () => { Bun.env.PI_PYTHON_SKIP_CHECK = "1"; using tempDir = TempDir.createSync("@omp-python-executor-per-call-"); const startupStarted = Promise.withResolvers(); PythonKernel.start = async options => { startupStarted.resolve(); return await rejectOnStartupCancellation(options); }; const abortController = new AbortController(); const resultPromise = executePython("sleep(10)", { kernelMode: "per-call", signal: abortController.signal, cwd: tempDir.path(), }); await startupStarted.promise; abortController.abort(createCancellationError("AbortError", "caller aborted")); const result = await resultPromise; expect(result.cancelled).toBe(true); expect(result.exitCode).toBeUndefined(); expect(result.output).toBe(""); }); it("shuts down kernel on timed-out cancellation", async () => { Bun.env.PI_PYTHON_SKIP_CHECK = "1"; using tempDir = TempDir.createSync("@omp-python-executor-per-call-"); let shutdownCalls = 0; let executeTimeoutMs: number | undefined; const kernel: KernelStub = { execute: async (_code: string, options?: KernelExecuteOptions) => { executeTimeoutMs = options?.timeoutMs; return { status: "ok", cancelled: true, timedOut: true, stdinRequested: false, }; }, shutdown: async () => { shutdownCalls += 1; }, }; PythonKernel.start = async () => kernel as unknown as PythonKernel; const result = await executePython("sleep(10)", { kernelMode: "per-call", timeoutMs: 2000, cwd: tempDir.path(), }); expect(result.cancelled).toBe(true); expect(result.exitCode).toBeUndefined(); expect(executeTimeoutMs).toBeGreaterThan(0); expect(executeTimeoutMs).toBeLessThanOrEqual(2000); const expectedSeconds = Math.max(1, Math.round((executeTimeoutMs ?? 0) / 1000)); expect(result.output).toContain(`eval cell timed out after ${expectedSeconds}s`); expect(shutdownCalls).toBe(1); }); });