PRA-only-demo/packages/rpa-core/test/engine/runtime.error-route.test.ts

77 lines
4.0 KiB
TypeScript

import { describe, expect, it } from "vitest";
import type { CatalogComponent, FlowDefinition } from "../../src/types";
import { register } from "../../src/engine/registry";
import { runFlow } from "../../src/engine/runtime";
const components = new Map<string, CatalogComponent>();
function component(id: string): CatalogComponent {
const value = {
id, name: id, kind: "vendor", group: "data", categories: [], categorySignature: "", description: "",
messageTemplate: "", baseType: id, functionName: id, functionCalls: [], version: "", tags: [], platformTag: null,
icon: "data", risk: "low", fields: [], output: { label: "", key: "", suggestedName: "", valueTypes: [], fields: [] }, slots: []
} as unknown as CatalogComponent;
components.set(id, value);
return value;
}
for (const id of ["Test.Start", "Test.Fail", "Test.Normal", "Test.Recover"]) component(id);
register("Test.Start", () => true);
register("Test.Fail", () => { throw new Error("expected failure"); });
register("Test.Normal", (ctx) => { ctx.vars.normalRan = true; });
register("Test.Recover", (ctx) => { ctx.vars.recovered = true; });
describe("runtime error route", () => {
it("uses the error handle after failure and does not execute the normal branch", async () => {
const now = new Date().toISOString();
const flow: FlowDefinition = {
schemaVersion: 1, id: "error-route", name: "error route", variables: [],
nodes: [
{ id: "start", componentId: "Test.Start", position: { x: 0, y: 0 }, values: {}, options: {} },
{ id: "fail", componentId: "Test.Fail", position: { x: 0, y: 100 }, values: {}, options: { errorRouteEnabled: true } },
{ id: "normal", componentId: "Test.Normal", position: { x: 0, y: 200 }, values: {}, options: {} },
{ id: "recover", componentId: "Test.Recover", position: { x: 200, y: 100 }, values: {}, options: {} }
],
edges: [
{ id: "e1", source: "start", target: "fail" },
{ id: "e2", source: "fail", target: "normal" },
{ id: "e3", source: "fail", sourceHandle: "error", target: "recover" }
],
meta: { createdAt: now, updatedAt: now, appVersion: "test" }
};
const result = await runFlow(flow, {
runId: "test", evidenceDir: ".", signal: new AbortController().signal,
emit: () => {}, log: () => {}, componentOf: (node) => components.get(node.componentId)
});
expect(result.status).toBe("succeeded");
expect(result.variables.recovered).toBe(true);
expect(result.variables.normalRan).toBeUndefined();
expect(result.variables.error).toMatchObject({ reason: "expected failure" });
});
it("uses the normal handle after success and leaves error and disconnected nodes untouched", async () => {
const now = new Date().toISOString();
const flow: FlowDefinition = {
schemaVersion: 1, id: "normal-route", name: "normal route", variables: [],
nodes: [
{ id: "start", componentId: "Test.Start", position: { x: 0, y: 0 }, values: {}, options: {} },
{ id: "normal", componentId: "Test.Normal", position: { x: 0, y: 100 }, values: {}, options: {} },
{ id: "recover", componentId: "Test.Recover", position: { x: 200, y: 0 }, values: {}, options: {} },
{ id: "orphan", componentId: "Test.Recover", position: { x: 400, y: 400 }, values: {}, options: {} }
],
edges: [
{ id: "e1", source: "start", target: "normal" },
{ id: "e2", source: "start", sourceHandle: "error", target: "recover" }
],
meta: { createdAt: now, updatedAt: now, appVersion: "test" }
};
const result = await runFlow(flow, {
runId: "test", evidenceDir: ".", signal: new AbortController().signal,
emit: () => {}, log: () => {}, componentOf: (node) => components.get(node.componentId)
});
expect(result.status).toBe("succeeded");
expect(result.variables.normalRan).toBe(true);
expect(result.variables.recovered).toBeUndefined();
expect(result.nodes.map((state) => state.nodeId)).toEqual(["start", "normal"]);
});
});