Files
united-workforce/packages/workflow-role-llm/__tests__/extract-meta.test.ts
T
xiaoju 82d3478895 refactor: extract @uncaged/workflow-util-role from role-llm (#15)
Move pure role utilities (decorateRole, withDryRun, onFail, schemaDefaults)
into @uncaged/workflow-util-role. extractMetaOrThrow stays in role-llm
since it depends on LLM capabilities.

Dependency graph (no cycles):
  util-role → workflow
  role-llm → workflow, util-role
  committer → workflow, util-role, role-llm

Closes #15
2026-05-06 07:27:11 +00:00

101 lines
2.6 KiB
TypeScript

import { describe, expect, test } from "bun:test";
import * as z from "zod/v4";
import { extractMetaOrThrow } from "../src/extract-meta.js";
const provider = {
baseUrl: "https://example.com/v1",
apiKey: "k",
model: "m",
};
describe("extractMetaOrThrow", () => {
const originalFetch = globalThis.fetch;
test("dryRun returns schema-shaped defaults without calling fetch", async () => {
let calls = 0;
globalThis.fetch = () => {
calls += 1;
return Promise.resolve(new Response("{}", { status: 200 }));
};
const schema = z.object({ n: z.number() });
const out = await extractMetaOrThrow("r", "raw", schema, {
provider,
dryRun: true,
});
globalThis.fetch = originalFetch;
expect(calls).toBe(0);
expect(out).toEqual({ n: 0 });
});
test("throws when extraction fails after retry", async () => {
globalThis.fetch = () =>
Promise.resolve(
new Response(
JSON.stringify({
choices: [
{
message: {
tool_calls: [
{ function: { name: "extract", arguments: JSON.stringify({ n: "bad" }) } },
],
},
},
],
}),
{ status: 200, headers: { "Content-Type": "application/json" } },
),
);
const schema = z.object({ n: z.number() });
await expect(
extractMetaOrThrow("plan", "text", schema, { provider, dryRun: false }),
).rejects.toThrow(/structured extraction failed after retry/);
globalThis.fetch = originalFetch;
});
test("returns validated meta on successful tool call", async () => {
globalThis.fetch = () =>
Promise.resolve(
new Response(
JSON.stringify({
choices: [
{
message: {
tool_calls: [
{
function: {
name: "extract",
arguments: JSON.stringify({ branch: "feat/x", message: "feat: y" }),
},
},
],
},
},
],
}),
{ status: 200, headers: { "Content-Type": "application/json" } },
),
);
const schema = z.object({
branch: z.string(),
message: z.string(),
});
const out = await extractMetaOrThrow("committer-plan", "plan text", schema, {
provider,
dryRun: false,
});
globalThis.fetch = originalFetch;
expect(out).toEqual({ branch: "feat/x", message: "feat: y" });
});
});