6efe86c07b
- Made PI_INTENT_TRACING, PI_AUTO_QA, PI_PY, and PI_JS take precedence when set. - Fell back to config when the env flag is unset instead of ORing. - Surfaced PI_PY=0/PI_JS=0 in the disabled-backend error messages.
146 lines
4.6 KiB
TypeScript
146 lines
4.6 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it, vi } from "bun:test";
|
|
import { Settings } from "@oh-my-pi/pi-coding-agent/config/settings";
|
|
import * as evalIndex from "@oh-my-pi/pi-coding-agent/eval";
|
|
import * as pyKernel from "@oh-my-pi/pi-coding-agent/eval/py/kernel";
|
|
import type { ToolSession } from "@oh-my-pi/pi-coding-agent/tools";
|
|
import { EvalTool } from "@oh-my-pi/pi-coding-agent/tools/eval";
|
|
import { resolveEvalBackends } from "@oh-my-pi/pi-coding-agent/tools/eval-backends";
|
|
|
|
let originalPiPy: string | undefined;
|
|
let originalPiJs: string | undefined;
|
|
|
|
function restoreEnv(name: "PI_PY" | "PI_JS", value: string | undefined): void {
|
|
if (value === undefined) {
|
|
delete Bun.env[name];
|
|
return;
|
|
}
|
|
Bun.env[name] = value;
|
|
}
|
|
function makeSession(settings = Settings.isolated()): ToolSession {
|
|
return {
|
|
cwd: "/tmp/eval-test",
|
|
hasUI: false,
|
|
getSessionFile: () => null,
|
|
getSessionSpawns: () => null,
|
|
settings,
|
|
};
|
|
}
|
|
|
|
const mockResult = {
|
|
output: "ok",
|
|
exitCode: 0,
|
|
cancelled: false,
|
|
truncated: false,
|
|
artifactId: undefined,
|
|
totalLines: 1,
|
|
totalBytes: 2,
|
|
outputLines: 1,
|
|
outputBytes: 2,
|
|
displayOutputs: [],
|
|
};
|
|
|
|
describe("EvalTool language dispatch", () => {
|
|
beforeEach(() => {
|
|
originalPiPy = Bun.env.PI_PY;
|
|
originalPiJs = Bun.env.PI_JS;
|
|
delete Bun.env.PI_PY;
|
|
delete Bun.env.PI_JS;
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.restoreAllMocks();
|
|
restoreEnv("PI_PY", originalPiPy);
|
|
restoreEnv("PI_JS", originalPiJs);
|
|
});
|
|
|
|
it('dispatches to the JS backend when cell.language === "js"', async () => {
|
|
const jsExecuteSpy = vi.spyOn(evalIndex.jsBackend, "execute").mockResolvedValue(mockResult);
|
|
const pythonExecuteSpy = vi.spyOn(evalIndex.pythonBackend, "execute");
|
|
|
|
const tool = new EvalTool(makeSession());
|
|
await tool.execute("call-js", {
|
|
cells: [{ language: "js", code: "const x = 1;" }],
|
|
});
|
|
|
|
expect(jsExecuteSpy).toHaveBeenCalledTimes(1);
|
|
expect(pythonExecuteSpy).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('dispatches to the Python backend when cell.language === "py"', async () => {
|
|
vi.spyOn(pyKernel, "checkPythonKernelAvailability").mockResolvedValue({ ok: true });
|
|
vi.spyOn(evalIndex.pythonBackend, "isAvailable").mockResolvedValue(true);
|
|
const pythonExecuteSpy = vi.spyOn(evalIndex.pythonBackend, "execute").mockResolvedValue(mockResult);
|
|
const jsExecuteSpy = vi.spyOn(evalIndex.jsBackend, "execute");
|
|
|
|
const tool = new EvalTool(makeSession());
|
|
await tool.execute("call-py", {
|
|
cells: [{ language: "py", code: "print('hi')" }],
|
|
});
|
|
|
|
expect(pythonExecuteSpy).toHaveBeenCalledTimes(1);
|
|
expect(jsExecuteSpy).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("interleaves backends across cells in a single call", async () => {
|
|
vi.spyOn(pyKernel, "checkPythonKernelAvailability").mockResolvedValue({ ok: true });
|
|
vi.spyOn(evalIndex.pythonBackend, "isAvailable").mockResolvedValue(true);
|
|
const pythonExecuteSpy = vi.spyOn(evalIndex.pythonBackend, "execute").mockResolvedValue(mockResult);
|
|
const jsExecuteSpy = vi.spyOn(evalIndex.jsBackend, "execute").mockResolvedValue(mockResult);
|
|
|
|
const tool = new EvalTool(makeSession());
|
|
await tool.execute("call-mixed", {
|
|
cells: [
|
|
{ language: "py", code: "x = 1" },
|
|
{ language: "js", code: "const y = 2;" },
|
|
],
|
|
});
|
|
|
|
expect(pythonExecuteSpy).toHaveBeenCalledTimes(1);
|
|
expect(jsExecuteSpy).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("rejects py cells when eval.py is disabled", async () => {
|
|
const settings = Settings.isolated();
|
|
settings.set("eval.py", false);
|
|
const tool = new EvalTool(makeSession(settings));
|
|
await expect(
|
|
tool.execute("call-py-disabled", {
|
|
cells: [{ language: "py", code: "print('hi')" }],
|
|
}),
|
|
).rejects.toThrow(/eval\.py = false/);
|
|
});
|
|
|
|
it("rejects js cells when eval.js is disabled", async () => {
|
|
const settings = Settings.isolated();
|
|
settings.set("eval.js", false);
|
|
const tool = new EvalTool(makeSession(settings));
|
|
await expect(
|
|
tool.execute("call-js-disabled", {
|
|
cells: [{ language: "js", code: "const x = 1;" }],
|
|
}),
|
|
).rejects.toThrow(/eval\.js = false/);
|
|
});
|
|
|
|
it("uses settings for eval backends whose env flag is unset", () => {
|
|
Bun.env.PI_PY = "1";
|
|
const settings = Settings.isolated();
|
|
settings.set("eval.py", false);
|
|
settings.set("eval.js", false);
|
|
|
|
expect(resolveEvalBackends(makeSession(settings))).toEqual({ python: true, js: false });
|
|
});
|
|
|
|
it("lets PI_JS disable js execution even when eval.js is enabled", async () => {
|
|
Bun.env.PI_JS = "0";
|
|
const settings = Settings.isolated();
|
|
settings.set("eval.js", true);
|
|
const tool = new EvalTool(makeSession(settings));
|
|
|
|
await expect(
|
|
tool.execute("call-js-env-disabled", {
|
|
cells: [{ language: "js", code: "const x = 1;" }],
|
|
}),
|
|
).rejects.toThrow(/PI_JS=0/);
|
|
});
|
|
});
|