test: make unsafe test coercion explicit

Move test coercion out of a hidden global and require each test to import the helper so review tools and runtime scripts can see the unsafe boundary.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
YeonGyu-Kim
2026-05-12 15:38:31 +09:00
parent ce5da13fc5
commit d5fbada13d
103 changed files with 700 additions and 554 deletions
+30 -8
View File
@@ -12,7 +12,8 @@
import { spawn } from "node:child_process";
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join, resolve } from "node:path";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
// ── CLI arg passthrough ───────────────────────────────────────
const extraArgs: string[] = [];
@@ -879,6 +880,7 @@ const TEST_CASES: TestCase[] = [
// ── JSONL event types ─────────────────────────────────────────
interface ToolCallEvent {
[key: string]: unknown;
tool_call_id: string;
tool_input: Record<string, unknown>;
tool_name: string;
@@ -886,6 +888,7 @@ interface ToolCallEvent {
}
interface ToolResultEvent {
[key: string]: unknown;
error?: string;
output: string;
tool_call_id: string;
@@ -897,6 +900,28 @@ interface AnyEvent {
[key: string]: unknown;
}
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null;
}
function isToolCallEvent(event: AnyEvent): event is ToolCallEvent {
return (
event.type === "tool_call" &&
typeof event.tool_call_id === "string" &&
typeof event.tool_name === "string" &&
isRecord(event.tool_input)
);
}
function isToolResultEvent(event: AnyEvent): event is ToolResultEvent {
return (
event.type === "tool_result" &&
typeof event.tool_call_id === "string" &&
typeof event.output === "string" &&
(event.error === undefined || typeof event.error === "string")
);
}
// ── Run single test case ─────────────────────────────────────
async function runTestCase(
tc: TestCase,
@@ -912,7 +937,8 @@ async function runTestCase(
writeFileSync(testFile, tc.fileContent, "utf-8");
}
const headlessScript = resolve(import.meta.dir, "headless.ts");
const currentDirectory = dirname(fileURLToPath(import.meta.url));
const headlessScript = resolve(currentDirectory, "headless.ts");
const headlessArgs = [
"run",
headlessScript,
@@ -975,12 +1001,8 @@ async function runTestCase(
}
}
const toolCalls = testCoerce<ToolCallEvent[]>(events.filter(
(e) => e.type === "tool_call"
));
const toolResults = testCoerce<ToolResultEvent[]>(events.filter(
(e) => e.type === "tool_result"
));
const toolCalls = events.filter(isToolCallEvent);
const toolResults = events.filter(isToolResultEvent);
const editCalls = toolCalls.filter((e) => e.tool_name === "edit_file");
const editCallIds = new Set(editCalls.map((e) => e.tool_call_id));
+30 -9
View File
@@ -12,7 +12,8 @@
import { spawn } from "node:child_process";
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join, resolve } from "node:path";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
// ── CLI arg passthrough ───────────────────────────────────────
const extraArgs: string[] = [];
@@ -37,7 +38,6 @@ for (let i = 0; i < rawArgs.length; i++) {
const BOLD = "\x1b[1m";
const GREEN = "\x1b[32m";
const RED = "\x1b[31m";
const YELLOW = "\x1b[33m";
const DIM = "\x1b[2m";
const CYAN = "\x1b[36m";
const RESET = "\x1b[0m";
@@ -574,6 +574,7 @@ const TEST_CASES: TestCase[] = [
// ── JSONL event types ─────────────────────────────────────────
interface ToolCallEvent {
[key: string]: unknown;
tool_call_id: string;
tool_input: Record<string, unknown>;
tool_name: string;
@@ -581,6 +582,7 @@ interface ToolCallEvent {
}
interface ToolResultEvent {
[key: string]: unknown;
error?: string;
output: string;
tool_call_id: string;
@@ -592,6 +594,28 @@ interface AnyEvent {
[key: string]: unknown;
}
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null;
}
function isToolCallEvent(event: AnyEvent): event is ToolCallEvent {
return (
event.type === "tool_call" &&
typeof event.tool_call_id === "string" &&
typeof event.tool_name === "string" &&
isRecord(event.tool_input)
);
}
function isToolResultEvent(event: AnyEvent): event is ToolResultEvent {
return (
event.type === "tool_result" &&
typeof event.tool_call_id === "string" &&
typeof event.output === "string" &&
(event.error === undefined || typeof event.error === "string")
);
}
// ── Run single test case ─────────────────────────────────────
async function runTestCase(
tc: TestCase,
@@ -605,7 +629,8 @@ async function runTestCase(
const testFile = join(testDir, tc.fileName);
writeFileSync(testFile, tc.fileContent, "utf-8");
const headlessScript = resolve(import.meta.dir, "headless.ts");
const currentDirectory = dirname(fileURLToPath(import.meta.url));
const headlessScript = resolve(currentDirectory, "headless.ts");
const headlessArgs = [
"run",
headlessScript,
@@ -668,12 +693,8 @@ async function runTestCase(
}
}
const toolCalls = testCoerce<ToolCallEvent[]>(events.filter(
(e) => e.type === "tool_call"
));
const toolResults = testCoerce<ToolResultEvent[]>(events.filter(
(e) => e.type === "tool_result"
));
const toolCalls = events.filter(isToolCallEvent);
const toolResults = events.filter(isToolResultEvent);
const editCalls = toolCalls.filter((e) => e.tool_name === "edit_file");
const editCallIds = new Set(editCalls.map((e) => e.tool_call_id));