This commit is contained in:
omp-port
2026-08-11 12:34:26 -04:00
parent 5b7796043e
commit 4742f11d35
10 changed files with 212 additions and 22 deletions

View File

@@ -10,12 +10,13 @@
import { describe, expect, it } from "bun:test";
import {
applySessionEvent,
emptySessionError,
type SessionEventAccumulator,
} from "../src/agent-runner.js";
import type { AgentSessionEvent } from "@oh-my-pi/pi-coding-agent";
function fresh(): SessionEventAccumulator {
return { text: "" };
return { text: "", sawMessage: false };
}
/** Build a typed-as-unknown event so malformed shapes compile in tests. */
@@ -134,4 +135,54 @@ describe("applySessionEvent", () => {
expect(forwarded).toEqual([]);
expect(acc.text).toBe("x");
});
it("marks sawMessage when any message or tool event is observed", () => {
const fromUpdate = fresh();
applySessionEvent(fromUpdate, event({ type: "message_update" }));
expect(fromUpdate.sawMessage).toBe(true);
const fromEnd = fresh();
applySessionEvent(fromEnd, event({ type: "message_end", message: null }));
expect(fromEnd.sawMessage).toBe(true);
const fromTool = fresh();
applySessionEvent(fromTool, event({ type: "tool_execution_start" }));
expect(fromTool.sawMessage).toBe(true);
});
});
describe("emptySessionError", () => {
it("fails a session that settled with no text and no observed events", () => {
expect(emptySessionError({ text: "", sawMessage: false })).toContain(
"no output",
);
});
it("fails an empty session whose final message reported stopReason error", () => {
expect(
emptySessionError({ text: "", sawMessage: true, stopReason: "error" }),
).toBe("sub-agent session ended in error with no output.");
});
it("surfaces a recorded errorMessage regardless of text", () => {
expect(
emptySessionError({
text: "partial output",
sawMessage: true,
errorMessage: "upstream 529",
}),
).toBe("upstream 529");
});
it("accepts an empty-text session that demonstrably ran (tool activity)", () => {
expect(
emptySessionError({ text: "", sawMessage: true, stopReason: "end_turn" }),
).toBeUndefined();
});
it("accepts any session with text", () => {
expect(
emptySessionError({ text: "report", sawMessage: false }),
).toBeUndefined();
});
});

View File

@@ -205,6 +205,30 @@ describe("detectTodoStubs", () => {
loud.some((h) => h.path.endsWith("fetch.rs") && h.snippet === "todo!("),
).toBe(true);
});
it("never descends into build/deploy output directories", async () => {
// Generated bundles under framework build dirs must not feed the
// pre-scan: they dominate candidate counts with minified noise (the
// freno-dev failure flagged 119 of 124 candidates inside
// `.output`/`.vercel` bundles, ballooning the scan task to 2.5 MB).
for (const rel of [
join(".output", "public", "bundle.js"),
join(".vercel", "output", "static", "app.js"),
join(".netlify", "functions", "bundle.js"),
]) {
const full = join(dir, rel);
await mkdir(join(full, ".."), { recursive: true });
await writeFile(
full,
"// TODO: bundle placeholder\nfunction f(){ return 0; }\nthrow new Error('not implemented');\n",
"utf8",
);
}
const hits = await detectTodoStubs(dir);
expect(
hits.filter((h) => /(?:\.output|\.vercel|\.netlify)[/\\]/.test(h.path)),
).toHaveLength(0);
});
});
describe("todos check", () => {
@@ -334,4 +358,25 @@ describe("todos check", () => {
);
expect(task).toContain("| new: 0 | resolved: 4 |");
});
it("truncates giant single-line candidates so the task prompt stays bounded", async () => {
// A minified/generated single line can be hundreds of KB; embedding it
// wholesale ballooned the freno-dev task to 2.5 MB and choked the
// analysis agent. The task must carry a truncated prefix, never the
// full line.
const long = `// TODO: ${"x".repeat(400)}`;
await writeFile(
join(cwd, "huge.ts"),
`${long}\nexport function f() { return 0; }\n`,
"utf8",
);
const task = await buildTodosScanTask(cwd, {
cwd,
target: cwd,
fix: false,
rest: [],
});
expect(task).not.toContain("x".repeat(400));
expect(task).toContain("…");
});
});