open-design/scripts/batch-design-system-test.test.ts
Marc Chan 055e55abd8
Add batch design system testing (#1515)
* feat: add batch design system testing

* fix: use daemon default agent for batch tests

* fix: honor batch project prompt flags

Generated-By: looper 0.0.0-dev (runner=fixer, agent=opencode)

* fix: persist batch run output

* fix: honor dry-run before daemon resolution

Generated-By: looper 0.0.0-dev (runner=fixer, agent=opencode)

* fix: persist batch assistant run ids

Generated-By: looper 0.0.0-dev (runner=fixer, agent=opencode)

* fix: cancel timed-out batch runs

Generated-By: looper 0.0.0-dev (runner=fixer, agent=opencode)
2026-05-14 14:19:32 +08:00

163 lines
4.8 KiB
TypeScript

import assert from "node:assert/strict";
import { spawnSync } from "node:child_process";
import path from "node:path";
import test from "node:test";
import { fileURLToPath } from "node:url";
import {
buildAssistantMessageUpdate,
cancelTimedOutRun,
createRunTimeoutError,
dedupeDesignSystemIds,
resolveDryRunDesignSystems,
validateExplicitDesignSystemIds,
} from "./batch-design-system-test.ts";
const HERE = path.dirname(fileURLToPath(import.meta.url));
test("dedupeDesignSystemIds trims and preserves first-seen order", () => {
assert.deepEqual(dedupeDesignSystemIds([" default ", "kami", "default", "", "kami "]), ["default", "kami"]);
});
test("validateExplicitDesignSystemIds rejects unknown design system ids", () => {
assert.throws(
() => validateExplicitDesignSystemIds(["default", "typo"], ["default", "kami"]),
/unknown design system id\(s\): typo/,
);
});
test("validateExplicitDesignSystemIds returns the normalized explicit ids when all exist", () => {
assert.deepEqual(validateExplicitDesignSystemIds([" default ", "kami", "default"], ["default", "kami"]), ["default", "kami"]);
});
test("resolveDryRunDesignSystems normalizes explicit ids without daemon access", () => {
assert.deepEqual(resolveDryRunDesignSystems({ designSystems: [" default ", "kami", "default"] }), ["default", "kami"]);
});
test("resolveDryRunDesignSystems rejects --all-design-systems in dry-run mode", () => {
assert.throws(
() => resolveDryRunDesignSystems({ allDesignSystems: true }),
/dry-run with --all-design-systems still requires daemon access/,
);
});
test("buildAssistantMessageUpdate persists the daemon run id on queued assistant rows", () => {
assert.deepEqual(
buildAssistantMessageUpdate({
agentId: "claude",
runId: "run-123",
runStatus: "queued",
createdAt: 123,
}),
{
role: "assistant",
content: "",
agentId: "claude",
agentName: "claude",
runId: "run-123",
runStatus: "queued",
startedAt: 123,
createdAt: 123,
},
);
});
test("buildAssistantMessageUpdate keeps the daemon run id on final assistant updates", () => {
assert.deepEqual(
buildAssistantMessageUpdate({
agentId: "claude",
runId: "run-123",
runStatus: "succeeded",
createdAt: 123,
content: "done",
endedAt: 456,
producedFiles: [{ path: "index.html" }],
}),
{
role: "assistant",
content: "done",
agentId: "claude",
agentName: "claude",
runId: "run-123",
runStatus: "succeeded",
startedAt: 123,
createdAt: 123,
endedAt: 456,
producedFiles: [{ path: "index.html" }],
},
);
});
test("cancelTimedOutRun posts to the daemon cancel endpoint for timeout errors", async () => {
const originalFetch = globalThis.fetch;
const calls: Array<{ url: string; init: RequestInit | undefined }> = [];
globalThis.fetch = (async (url: string | URL | Request, init?: RequestInit) => {
calls.push({ url: String(url), init });
return new Response(JSON.stringify({ ok: true }), {
status: 200,
headers: { "content-type": "application/json" },
});
}) as typeof fetch;
try {
const canceled = await cancelTimedOutRun("http://127.0.0.1:4321", createRunTimeoutError("run-123", 5000, "running"));
assert.equal(canceled, true);
assert.deepEqual(calls, [
{
url: "http://127.0.0.1:4321/api/runs/run-123/cancel",
init: {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({}),
},
},
]);
} finally {
globalThis.fetch = originalFetch;
}
});
test("cancelTimedOutRun ignores non-timeout errors", async () => {
const originalFetch = globalThis.fetch;
let called = false;
globalThis.fetch = (async () => {
called = true;
throw new Error("should not be called");
}) as typeof fetch;
try {
const canceled = await cancelTimedOutRun("http://127.0.0.1:4321", new Error("not a timeout"));
assert.equal(canceled, false);
assert.equal(called, false);
} finally {
globalThis.fetch = originalFetch;
}
});
test("CLI dry-run with explicit design systems succeeds without daemon discovery", () => {
const result = spawnSync(
process.execPath,
[
"--experimental-strip-types",
path.join(HERE, "batch-design-system-test.ts"),
"--prompt",
"Test prompt",
"--design-systems",
"default,kami",
"--dry-run",
],
{
cwd: path.resolve(HERE, ".."),
encoding: "utf8",
env: {
...process.env,
OD_DAEMON_URL: "",
OD_PORT: "",
},
},
);
assert.equal(result.status, 0, result.stderr || result.stdout);
assert.match(result.stdout, /design systems \(2\): default, kami/);
assert.doesNotMatch(result.stdout + result.stderr, /cannot determine daemon URL/);
});