2a4d35399b
5 tests passing: transition, condition match, fallback, missing role error, output expansion. Refs #309, #311
121 lines
3.4 KiB
TypeScript
121 lines
3.4 KiB
TypeScript
import { describe, expect, test } from "bun:test";
|
|
import type { ModeratorContext, WorkflowPayload } from "@uncaged/uwf-protocol";
|
|
|
|
import { evaluate } from "../src/evaluate.js";
|
|
|
|
const solveIssueWorkflow: WorkflowPayload = {
|
|
name: "solve-issue",
|
|
description: "End-to-end issue resolution",
|
|
roles: {
|
|
planner: {
|
|
description: "Creates implementation plan",
|
|
systemPrompt: "You are a planning agent...",
|
|
outputSchema: "5GWKR8TN1V3JA",
|
|
},
|
|
developer: {
|
|
description: "Implements code changes",
|
|
systemPrompt: "You are a developer agent...",
|
|
outputSchema: "8CNWT4KR6D1HV",
|
|
},
|
|
reviewer: {
|
|
description: "Reviews code changes",
|
|
systemPrompt: "You are a code reviewer...",
|
|
outputSchema: "1VPBG9SM5E7WK",
|
|
},
|
|
},
|
|
conditions: {
|
|
needsClarification: {
|
|
description: "Planner requests clarification from user",
|
|
expression: "$exists(steps[-1].output.needsClarification)",
|
|
},
|
|
notApproved: {
|
|
description: "Reviewer rejected the implementation",
|
|
expression: "steps[-1].output.approved = false",
|
|
},
|
|
},
|
|
graph: {
|
|
$START: [{ role: "planner", condition: null }],
|
|
planner: [
|
|
{ role: "developer", condition: "needsClarification" },
|
|
{ role: "$END", condition: null },
|
|
],
|
|
developer: [{ role: "reviewer", condition: null }],
|
|
reviewer: [
|
|
{ role: "developer", condition: "notApproved" },
|
|
{ role: "$END", condition: null },
|
|
],
|
|
},
|
|
};
|
|
|
|
function makeContext(steps: ModeratorContext["steps"]): ModeratorContext {
|
|
return {
|
|
start: {
|
|
workflow: "4KNM2PXR3B1QW",
|
|
prompt: "Fix the login bug",
|
|
},
|
|
steps,
|
|
};
|
|
}
|
|
|
|
describe("evaluate", () => {
|
|
test("$START → first role (fallback)", () => {
|
|
const result = evaluate(solveIssueWorkflow, makeContext([]));
|
|
expect(result).toEqual({ ok: true, value: "planner" });
|
|
});
|
|
|
|
test("condition match (notApproved → developer)", () => {
|
|
const context = makeContext([
|
|
{
|
|
role: "reviewer",
|
|
output: { approved: false },
|
|
detail: "2MXBG6PN4A8JR",
|
|
agent: "uwf-hermes",
|
|
},
|
|
]);
|
|
const result = evaluate(solveIssueWorkflow, context);
|
|
expect(result).toEqual({ ok: true, value: "developer" });
|
|
});
|
|
|
|
test("fallback when condition does not match → $END", () => {
|
|
const context = makeContext([
|
|
{
|
|
role: "reviewer",
|
|
output: { approved: true },
|
|
detail: "2MXBG6PN4A8JR",
|
|
agent: "uwf-hermes",
|
|
},
|
|
]);
|
|
const result = evaluate(solveIssueWorkflow, context);
|
|
expect(result).toEqual({ ok: true, value: "$END" });
|
|
});
|
|
|
|
test("missing role in graph → error", () => {
|
|
const context = makeContext([
|
|
{
|
|
role: "unknown-role",
|
|
output: {},
|
|
detail: "2MXBG6PN4A8JR",
|
|
agent: "uwf-hermes",
|
|
},
|
|
]);
|
|
const result = evaluate(solveIssueWorkflow, context);
|
|
expect(result.ok).toBe(false);
|
|
if (!result.ok) {
|
|
expect(result.error.message).toBe('no transitions defined for role "unknown-role"');
|
|
}
|
|
});
|
|
|
|
test("output expansion in context works with JSONata", () => {
|
|
const context = makeContext([
|
|
{
|
|
role: "planner",
|
|
output: { needsClarification: true },
|
|
detail: "7BQST3VW9F2MA",
|
|
agent: "uwf-hermes",
|
|
},
|
|
]);
|
|
const result = evaluate(solveIssueWorkflow, context);
|
|
expect(result).toEqual({ ok: true, value: "developer" });
|
|
});
|
|
});
|