Files
ai-agent/web/app/dashboard/ai-workflows/_components/workflow-utils.test.mjs
T
2026-06-22 00:26:16 +08:00

128 lines
3.4 KiB
JavaScript

import assert from "node:assert/strict"
import { describe, it } from "node:test"
import ts from "typescript"
import { readFile } from "node:fs/promises"
import vm from "node:vm"
function plain(value) {
return JSON.parse(JSON.stringify(value))
}
async function loadModule() {
const source = await readFile(new URL("./workflow-utils.ts", import.meta.url), "utf8")
const compiled = ts.transpileModule(source, {
compilerOptions: {
target: ts.ScriptTarget.ES2017,
module: ts.ModuleKind.CommonJS,
},
fileName: "workflow-utils.ts",
})
const sandbox = {
exports: {},
module: { exports: {} },
}
sandbox.exports = sandbox.module.exports
vm.runInNewContext(compiled.outputText, sandbox)
return sandbox.module.exports
}
describe("validateWorkflowDraft", () => {
it("rejects missing start", async () => {
const { validateWorkflowDraft } = await loadModule()
const result = validateWorkflowDraft({
nodes: [{ id: "end_1", type: "end", position: { x: 0, y: 0 }, data: {} }],
edges: [],
})
assert.equal(result.valid, false)
assert.match(result.errors.join("\n"), /exactly one start/)
})
it("rejects dangling edge", async () => {
const { validateWorkflowDraft } = await loadModule()
const result = validateWorkflowDraft({
nodes: [
{ id: "start_1", type: "start", position: { x: 0, y: 0 }, data: {} },
{ id: "end_1", type: "end", position: { x: 200, y: 0 }, data: {} },
],
edges: [{ id: "e1", source: "start_1", target: "missing_1" }],
})
assert.equal(result.valid, false)
assert.match(result.errors.join("\n"), /target node does not exist/)
})
})
describe("toApiDefinition", () => {
it("preserves xyflow node positions", async () => {
const { toApiDefinition } = await loadModule()
const definition = toApiDefinition({
nodes: [
{
id: "start_1",
type: "start",
position: { x: 12, y: 34 },
data: { name: "Start", config: { enabled: true } },
},
{
id: "end_1",
type: "end",
position: { x: 240, y: 80 },
data: { name: "End", config: {} },
},
],
edges: [{ id: "e1", source: "start_1", target: "end_1" }],
})
assert.deepEqual(plain(definition), {
schemaVersion: 1,
entryNodeId: "start_1",
nodes: [
{
id: "start_1",
type: "start",
name: "Start",
position: { x: 12, y: 34 },
config: { enabled: true },
},
{
id: "end_1",
type: "end",
name: "End",
position: { x: 240, y: 80 },
config: {},
},
],
edges: [{ id: "e1", source: "start_1", target: "end_1" }],
})
})
it("uses node data type for xyflow default nodes", async () => {
const { toApiDefinition } = await loadModule()
const definition = toApiDefinition({
nodes: [
{
id: "start_1",
type: "default",
position: { x: 0, y: 0 },
data: { nodeType: "start", name: "Start", config: {} },
},
{
id: "end_1",
type: "default",
position: { x: 200, y: 0 },
data: { nodeType: "end", name: "End", config: {} },
},
],
edges: [{ id: "e1", source: "start_1", target: "end_1" }],
})
assert.equal(definition.entryNodeId, "start_1")
assert.equal(definition.nodes[0].type, "start")
})
})