Files
openclaw/src/agents/code-mode.typescript.test.ts
2026-07-29 15:26:00 +08:00

185 lines
6.5 KiB
TypeScript

/** Tests Code Mode TypeScript execution. */
import { expectDefined } from "@openclaw/normalization-core";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { applyCodeModeCatalog } from "./code-mode.js";
import {
resetCodeModeTestState,
pluginTool,
resultDetails,
createCodeModeHarness,
runUntilCompleted,
testing,
} from "./code-mode.test-support.js";
describe("Code Mode TypeScript execution", () => {
beforeEach(() => {
vi.useRealTimers();
});
afterEach(() => {
vi.useRealTimers();
resetCodeModeTestState();
});
it("supports TypeScript source transform", async () => {
testing.setTypescriptRuntimeForTest({
...(await import("typescript")),
transpileModule: vi.fn((code: string) => ({
outputText: code.replace(": number", ""),
diagnostics: [],
})),
ScriptTarget: { ES2022: 9 },
ModuleKind: { ESNext: 99 },
ImportsNotUsedAsValues: { Remove: 0 },
DiagnosticCategory: { Error: 1 },
flattenDiagnosticMessageText: (message: unknown) => String(message),
} as never);
const { config, catalogRef, tools: codeModeTools } = createCodeModeHarness();
applyCodeModeCatalog({
tools: [...codeModeTools, pluginTool("fake_noop", "Noop")],
config,
sessionId: "session-code-mode",
sessionKey: "agent:main:main",
runId: "run-code-mode",
catalogRef,
});
const details = await runUntilCompleted({
execTool: expectDefined(codeModeTools[0], "codeModeTools[0] test invariant"),
waitTool: expectDefined(codeModeTools[1], "codeModeTools[1] test invariant"),
language: "typescript",
code: `
const value: number = 40 + 2;
return { value };
`,
});
expect(details.status).toBe("completed");
expect(details.value).toEqual({ value: 42 });
const moduleShapedTypeScript = await runUntilCompleted({
execTool: expectDefined(codeModeTools[0], "codeModeTools[0] test invariant"),
waitTool: expectDefined(codeModeTools[1], "codeModeTools[1] test invariant"),
language: "typescript",
code: "const value: number = 42; return `import('node:fs') ${value}`;",
});
expect(moduleShapedTypeScript.status).toBe("completed");
expect(moduleShapedTypeScript.value).toBe("import('node:fs') 42");
const moduleShapedTypeScriptRegex = await runUntilCompleted({
execTool: expectDefined(codeModeTools[0], "codeModeTools[0] test invariant"),
waitTool: expectDefined(codeModeTools[1], "codeModeTools[1] test invariant"),
language: "typescript",
code: 'const value: number = 42; return /import.meta/.test("import.meta");',
});
expect(moduleShapedTypeScriptRegex.status).toBe("completed");
expect(moduleShapedTypeScriptRegex.value).toBe(true);
for (const moduleAccess of ["import('node:fs')", "require('node:fs')"]) {
const unicodeModuleAccess = resultDetails(
await expectDefined(codeModeTools[0], "codeModeTools[0] test invariant").execute(
`code-call-typescript-unicode-${moduleAccess.startsWith("import") ? "import" : "require"}`,
{
language: "typescript",
code: `const value: number = 1; const padding = "${"😀".repeat(96)}"; return ${moduleAccess};`,
},
),
);
expect(unicodeModuleAccess.status).toBe("failed");
expect(unicodeModuleAccess.code).toBe("invalid_input");
expect(unicodeModuleAccess.error).toContain("module access is disabled");
}
const commandLikeTypeScript = await runUntilCompleted({
execTool: expectDefined(codeModeTools[0], "codeModeTools[0] test invariant"),
waitTool: expectDefined(codeModeTools[1], "codeModeTools[1] test invariant"),
language: "typescript",
code: "node -1; var node: number = 7; return node;",
});
expect(commandLikeTypeScript.status).toBe("completed");
expect(commandLikeTypeScript.value).toBe(7);
const typedShell = resultDetails(
await expectDefined(codeModeTools[0], "codeModeTools[0] test invariant").execute(
"code-call-typescript-shell-source",
{ code: "pwd", language: "typescript" },
),
);
expect(typedShell.status).toBe("failed");
expect(typedShell.code).toBe("invalid_input");
expect(typedShell.error).toMatch(/JavaScript or TypeScript, not shell commands/);
expect(testing.activeRuns.size).toBe(0);
});
it("times out an unfinished TypeScript runtime load without starting a worker", async () => {
const { config, catalogRef, tools: codeModeTools } = createCodeModeHarness();
(config as { tools: { codeMode: unknown } }).tools.codeMode = {
enabled: true,
timeoutMs: 100,
};
applyCodeModeCatalog({
tools: [...codeModeTools, pluginTool("fake_noop", "Noop")],
config,
sessionId: "session-code-mode",
sessionKey: "agent:main:main",
runId: "run-code-mode",
catalogRef,
});
testing.setTypescriptRuntimeForTest(new Promise<typeof import("typescript")>(() => {}));
const result = resultDetails(
await expectDefined(codeModeTools[0], "Code Mode exec test invariant").execute(
"code-call-typescript-load-timeout",
{ code: "return 42;", language: "typescript" },
),
);
expect(result).toMatchObject({
status: "failed",
code: "timeout",
error: "code mode timeout exceeded",
failurePhase: "host",
bridgeDispatchStarted: false,
output: [],
});
expect(testing.activeRuns.size).toBe(0);
});
it("aborts an unfinished TypeScript runtime load without starting a worker", async () => {
const { config, catalogRef, tools: codeModeTools } = createCodeModeHarness();
applyCodeModeCatalog({
tools: [...codeModeTools, pluginTool("fake_noop", "Noop")],
config,
sessionId: "session-code-mode",
sessionKey: "agent:main:main",
runId: "run-code-mode",
catalogRef,
});
testing.setTypescriptRuntimeForTest(new Promise<typeof import("typescript")>(() => {}));
const controller = new AbortController();
const resultPromise = expectDefined(codeModeTools[0], "Code Mode exec test invariant").execute(
"code-call-typescript-load-abort",
{ code: "return 42;", language: "typescript" },
controller.signal,
);
controller.abort();
expect(resultDetails(await resultPromise)).toMatchObject({
status: "failed",
code: "aborted",
error: "code mode execution aborted",
failurePhase: "host",
bridgeDispatchStarted: false,
output: [],
});
expect(testing.activeRuns.size).toBe(0);
});
});