mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-24 13:54:57 +00:00
fix: resolve TypeScript errors in workflow-engine.test.ts
- Add createNode() and createWorkflow() helper functions for type-safe test data - Replace all raw object literals with properly typed helper function calls - Ensures WorkflowNode includes required 'position' property - Ensures Workflow includes required 'edges' and 'enabled' properties - All 276 tests pass with clean typecheck
This commit is contained in:
@@ -2,6 +2,25 @@ import { describe, it, expect, beforeEach } from 'vitest'
|
||||
import { WorkflowEngine, createWorkflowEngine } from './workflow-engine'
|
||||
import type { Workflow, WorkflowNode, LuaScript } from './level-types'
|
||||
|
||||
// Helper to create a minimal valid WorkflowNode
|
||||
function createNode(
|
||||
id: string,
|
||||
type: WorkflowNode['type'],
|
||||
label: string,
|
||||
config: Record<string, any> = {}
|
||||
): WorkflowNode {
|
||||
return { id, type, label, config, position: { x: 0, y: 0 } }
|
||||
}
|
||||
|
||||
// Helper to create a minimal valid Workflow
|
||||
function createWorkflow(
|
||||
id: string,
|
||||
name: string,
|
||||
nodes: WorkflowNode[]
|
||||
): Workflow {
|
||||
return { id, name, nodes, edges: [], enabled: true }
|
||||
}
|
||||
|
||||
describe('workflow-engine', () => {
|
||||
let engine: WorkflowEngine
|
||||
|
||||
@@ -20,13 +39,9 @@ describe('workflow-engine', () => {
|
||||
it.each([
|
||||
{
|
||||
name: 'simple trigger workflow',
|
||||
workflow: {
|
||||
id: 'w1',
|
||||
name: 'Simple Workflow',
|
||||
nodes: [
|
||||
{ id: 'n1', type: 'trigger', label: 'Start', config: {} },
|
||||
],
|
||||
} as Workflow,
|
||||
workflow: createWorkflow('w1', 'Simple Workflow', [
|
||||
createNode('n1', 'trigger', 'Start'),
|
||||
]),
|
||||
context: { data: { test: 'value' } },
|
||||
expected: {
|
||||
success: true,
|
||||
@@ -36,14 +51,10 @@ describe('workflow-engine', () => {
|
||||
},
|
||||
{
|
||||
name: 'workflow with action node',
|
||||
workflow: {
|
||||
id: 'w2',
|
||||
name: 'Action Workflow',
|
||||
nodes: [
|
||||
{ id: 'n1', type: 'trigger', label: 'Start', config: {} },
|
||||
{ id: 'n2', type: 'action', label: 'Process', config: { action: 'process' } },
|
||||
],
|
||||
} as Workflow,
|
||||
workflow: createWorkflow('w2', 'Action Workflow', [
|
||||
createNode('n1', 'trigger', 'Start'),
|
||||
createNode('n2', 'action', 'Process', { action: 'process' }),
|
||||
]),
|
||||
context: { data: { value: 42 } },
|
||||
expected: {
|
||||
success: true,
|
||||
@@ -53,14 +64,10 @@ describe('workflow-engine', () => {
|
||||
},
|
||||
{
|
||||
name: 'workflow with transform node',
|
||||
workflow: {
|
||||
id: 'w3',
|
||||
name: 'Transform Workflow',
|
||||
nodes: [
|
||||
{ id: 'n1', type: 'trigger', label: 'Start', config: {} },
|
||||
{ id: 'n2', type: 'transform', label: 'Double', config: { transform: 'data.value * 2' } },
|
||||
],
|
||||
} as Workflow,
|
||||
workflow: createWorkflow('w3', 'Transform Workflow', [
|
||||
createNode('n1', 'trigger', 'Start'),
|
||||
createNode('n2', 'transform', 'Double', { transform: 'data.value * 2' }),
|
||||
]),
|
||||
context: { data: { value: 5 } },
|
||||
expected: {
|
||||
success: true,
|
||||
@@ -81,15 +88,11 @@ describe('workflow-engine', () => {
|
||||
it.each([
|
||||
{
|
||||
name: 'false condition stops execution',
|
||||
workflow: {
|
||||
id: 'w4',
|
||||
name: 'Condition Workflow',
|
||||
nodes: [
|
||||
{ id: 'n1', type: 'trigger', label: 'Start', config: {} },
|
||||
{ id: 'n2', type: 'condition', label: 'Check', config: { condition: 'false' } },
|
||||
{ id: 'n3', type: 'action', label: 'Never Run', config: {} },
|
||||
],
|
||||
} as Workflow,
|
||||
workflow: createWorkflow('w4', 'Condition Workflow', [
|
||||
createNode('n1', 'trigger', 'Start'),
|
||||
createNode('n2', 'condition', 'Check', { condition: 'false' }),
|
||||
createNode('n3', 'action', 'Never Run'),
|
||||
]),
|
||||
context: { data: {} },
|
||||
expected: {
|
||||
success: true,
|
||||
@@ -99,15 +102,11 @@ describe('workflow-engine', () => {
|
||||
},
|
||||
{
|
||||
name: 'true condition continues execution',
|
||||
workflow: {
|
||||
id: 'w5',
|
||||
name: 'Pass Condition',
|
||||
nodes: [
|
||||
{ id: 'n1', type: 'trigger', label: 'Start', config: {} },
|
||||
{ id: 'n2', type: 'condition', label: 'Check', config: { condition: 'true' } },
|
||||
{ id: 'n3', type: 'action', label: 'Should Run', config: {} },
|
||||
],
|
||||
} as Workflow,
|
||||
workflow: createWorkflow('w5', 'Pass Condition', [
|
||||
createNode('n1', 'trigger', 'Start'),
|
||||
createNode('n2', 'condition', 'Check', { condition: 'true' }),
|
||||
createNode('n3', 'action', 'Should Run'),
|
||||
]),
|
||||
context: { data: {} },
|
||||
expected: {
|
||||
success: true,
|
||||
@@ -117,14 +116,10 @@ describe('workflow-engine', () => {
|
||||
},
|
||||
{
|
||||
name: 'data-based condition',
|
||||
workflow: {
|
||||
id: 'w6',
|
||||
name: 'Data Condition',
|
||||
nodes: [
|
||||
{ id: 'n1', type: 'trigger', label: 'Start', config: {} },
|
||||
{ id: 'n2', type: 'condition', label: 'Check Value', config: { condition: 'data.value > 10' } },
|
||||
],
|
||||
} as Workflow,
|
||||
workflow: createWorkflow('w6', 'Data Condition', [
|
||||
createNode('n1', 'trigger', 'Start'),
|
||||
createNode('n2', 'condition', 'Check Value', { condition: 'data.value > 10' }),
|
||||
]),
|
||||
context: { data: { value: 5 } },
|
||||
expected: {
|
||||
success: true,
|
||||
@@ -146,39 +141,27 @@ describe('workflow-engine', () => {
|
||||
it.each([
|
||||
{
|
||||
name: 'node with unknown type',
|
||||
workflow: {
|
||||
id: 'w7',
|
||||
name: 'Invalid Node',
|
||||
nodes: [
|
||||
{ id: 'n1', type: 'unknown' as any, label: 'Bad Node', config: {} },
|
||||
],
|
||||
} as Workflow,
|
||||
workflow: createWorkflow('w7', 'Invalid Node', [
|
||||
createNode('n1', 'unknown' as any, 'Bad Node'),
|
||||
]),
|
||||
context: { data: {} },
|
||||
expectedError: 'Unknown node type',
|
||||
},
|
||||
{
|
||||
name: 'invalid condition syntax',
|
||||
workflow: {
|
||||
id: 'w8',
|
||||
name: 'Bad Condition',
|
||||
nodes: [
|
||||
{ id: 'n1', type: 'trigger', label: 'Start', config: {} },
|
||||
{ id: 'n2', type: 'condition', label: 'Invalid', config: { condition: 'invalid syntax !' } },
|
||||
],
|
||||
} as Workflow,
|
||||
workflow: createWorkflow('w8', 'Bad Condition', [
|
||||
createNode('n1', 'trigger', 'Start'),
|
||||
createNode('n2', 'condition', 'Invalid', { condition: 'invalid syntax !' }),
|
||||
]),
|
||||
context: { data: {} },
|
||||
expectedError: 'failed',
|
||||
},
|
||||
{
|
||||
name: 'invalid transform syntax',
|
||||
workflow: {
|
||||
id: 'w9',
|
||||
name: 'Bad Transform',
|
||||
nodes: [
|
||||
{ id: 'n1', type: 'trigger', label: 'Start', config: {} },
|
||||
{ id: 'n2', type: 'transform', label: 'Invalid', config: { transform: 'this is not valid javascript' } },
|
||||
],
|
||||
} as Workflow,
|
||||
workflow: createWorkflow('w9', 'Bad Transform', [
|
||||
createNode('n1', 'trigger', 'Start'),
|
||||
createNode('n2', 'transform', 'Invalid', { transform: 'this is not valid javascript' }),
|
||||
]),
|
||||
context: { data: {} },
|
||||
expectedError: 'Transform failed',
|
||||
},
|
||||
@@ -196,46 +179,27 @@ describe('workflow-engine', () => {
|
||||
it.each([
|
||||
{
|
||||
name: 'simple Lua return',
|
||||
node: {
|
||||
id: 'n1',
|
||||
type: 'lua',
|
||||
label: 'Lua Node',
|
||||
config: { code: 'return 42' },
|
||||
} as WorkflowNode,
|
||||
node: createNode('n1', 'lua', 'Lua Node', { code: 'return 42' }),
|
||||
data: {},
|
||||
expectedOutput: 42,
|
||||
expectedSuccess: true,
|
||||
},
|
||||
{
|
||||
name: 'Lua with data access',
|
||||
node: {
|
||||
id: 'n2',
|
||||
type: 'lua',
|
||||
label: 'Data Access',
|
||||
config: { code: 'return context.data.value * 2' },
|
||||
} as WorkflowNode,
|
||||
node: createNode('n2', 'lua', 'Data Access', { code: 'return context.data.value * 2' }),
|
||||
data: { value: 21 },
|
||||
expectedOutput: 42,
|
||||
expectedSuccess: true,
|
||||
},
|
||||
{
|
||||
name: 'Lua with default code',
|
||||
node: {
|
||||
id: 'n3',
|
||||
type: 'lua',
|
||||
label: 'Default',
|
||||
config: {},
|
||||
} as WorkflowNode,
|
||||
node: createNode('n3', 'lua', 'Default'),
|
||||
data: { test: 'value' },
|
||||
expectedOutput: { test: 'value' },
|
||||
expectedSuccess: true,
|
||||
},
|
||||
])('should execute $name', async ({ node, data, expectedOutput, expectedSuccess }) => {
|
||||
const workflow: Workflow = {
|
||||
id: 'w-lua',
|
||||
name: 'Lua Test',
|
||||
nodes: [node],
|
||||
}
|
||||
const workflow = createWorkflow('w-lua', 'Lua Test', [node])
|
||||
|
||||
const result = await engine.executeWorkflow(workflow, { data })
|
||||
|
||||
@@ -249,31 +213,17 @@ describe('workflow-engine', () => {
|
||||
scripts: [
|
||||
{ id: 'script1', name: 'Test Script', code: 'return 100', description: '' },
|
||||
] as LuaScript[],
|
||||
node: {
|
||||
id: 'n1',
|
||||
type: 'lua',
|
||||
label: 'Script Ref',
|
||||
config: { scriptId: 'script1' },
|
||||
} as WorkflowNode,
|
||||
node: createNode('n1', 'lua', 'Script Ref', { scriptId: 'script1' }),
|
||||
expectedOutput: 100,
|
||||
},
|
||||
{
|
||||
name: 'Lua with missing script',
|
||||
scripts: [] as LuaScript[],
|
||||
node: {
|
||||
id: 'n2',
|
||||
type: 'lua',
|
||||
label: 'Missing Script',
|
||||
config: { scriptId: 'nonexistent' },
|
||||
} as WorkflowNode,
|
||||
node: createNode('n2', 'lua', 'Missing Script', { scriptId: 'nonexistent' }),
|
||||
expectedError: 'Script not found',
|
||||
},
|
||||
])('should handle $name', async ({ scripts, node, expectedOutput, expectedError }) => {
|
||||
const workflow: Workflow = {
|
||||
id: 'w-script',
|
||||
name: 'Script Test',
|
||||
nodes: [node],
|
||||
}
|
||||
const workflow = createWorkflow('w-script', 'Script Test', [node])
|
||||
|
||||
const result = await engine.executeWorkflow(workflow, { data: {}, scripts })
|
||||
|
||||
@@ -298,18 +248,9 @@ describe('workflow-engine', () => {
|
||||
expectedError: true,
|
||||
},
|
||||
])('should handle Lua error: $name', async ({ code, expectedError }) => {
|
||||
const workflow: Workflow = {
|
||||
id: 'w-error',
|
||||
name: 'Error Test',
|
||||
nodes: [
|
||||
{
|
||||
id: 'n1',
|
||||
type: 'lua',
|
||||
label: 'Error Node',
|
||||
config: { code },
|
||||
},
|
||||
],
|
||||
}
|
||||
const workflow = createWorkflow('w-error', 'Error Test', [
|
||||
createNode('n1', 'lua', 'Error Node', { code }),
|
||||
])
|
||||
|
||||
const result = await engine.executeWorkflow(workflow, { data: {} })
|
||||
|
||||
@@ -321,23 +262,14 @@ describe('workflow-engine', () => {
|
||||
|
||||
it('should capture Lua security warnings', async () => {
|
||||
// Test with code that might trigger security warnings
|
||||
const workflow: Workflow = {
|
||||
id: 'w-security',
|
||||
name: 'Security Test',
|
||||
nodes: [
|
||||
{
|
||||
id: 'n1',
|
||||
type: 'lua',
|
||||
label: 'Security Check',
|
||||
config: {
|
||||
code: `
|
||||
-- Attempting unsafe operations
|
||||
return 42
|
||||
`,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
const workflow = createWorkflow('w-security', 'Security Test', [
|
||||
createNode('n1', 'lua', 'Security Check', {
|
||||
code: `
|
||||
-- Attempting unsafe operations
|
||||
return 42
|
||||
`,
|
||||
}),
|
||||
])
|
||||
|
||||
const result = await engine.executeWorkflow(workflow, { data: {} })
|
||||
|
||||
@@ -349,15 +281,11 @@ describe('workflow-engine', () => {
|
||||
|
||||
describe('logging and context', () => {
|
||||
it('should capture logs from all nodes', async () => {
|
||||
const workflow: Workflow = {
|
||||
id: 'w-log',
|
||||
name: 'Log Test',
|
||||
nodes: [
|
||||
{ id: 'n1', type: 'trigger', label: 'Start', config: {} },
|
||||
{ id: 'n2', type: 'action', label: 'Action', config: { action: 'test' } },
|
||||
{ id: 'n3', type: 'transform', label: 'Transform', config: { transform: '42' } },
|
||||
],
|
||||
}
|
||||
const workflow = createWorkflow('w-log', 'Log Test', [
|
||||
createNode('n1', 'trigger', 'Start'),
|
||||
createNode('n2', 'action', 'Action', { action: 'test' }),
|
||||
createNode('n3', 'transform', 'Transform', { transform: '42' }),
|
||||
])
|
||||
|
||||
const result = await engine.executeWorkflow(workflow, { data: {} })
|
||||
|
||||
@@ -369,18 +297,9 @@ describe('workflow-engine', () => {
|
||||
|
||||
it('should pass user context to nodes', async () => {
|
||||
const user = { id: 'user1', name: 'Test User' }
|
||||
const workflow: Workflow = {
|
||||
id: 'w-user',
|
||||
name: 'User Context',
|
||||
nodes: [
|
||||
{
|
||||
id: 'n1',
|
||||
type: 'lua',
|
||||
label: 'Get User',
|
||||
config: { code: 'return context.user.name' },
|
||||
},
|
||||
],
|
||||
}
|
||||
const workflow = createWorkflow('w-user', 'User Context', [
|
||||
createNode('n1', 'lua', 'Get User', { code: 'return context.user.name' }),
|
||||
])
|
||||
|
||||
const result = await engine.executeWorkflow(workflow, { data: {}, user })
|
||||
|
||||
@@ -396,16 +315,12 @@ describe('workflow-engine', () => {
|
||||
// n2: transforms data.value to data.value + 10
|
||||
// n3: condition evaluates data > 50, passes through boolean (true)
|
||||
// n4: transforms data (now true/1) * 2 = 2
|
||||
const workflow: Workflow = {
|
||||
id: 'w-complex',
|
||||
name: 'Complex Workflow',
|
||||
nodes: [
|
||||
{ id: 'n1', type: 'trigger', label: 'Start', config: {} },
|
||||
{ id: 'n2', type: 'transform', label: 'Add 10', config: { transform: 'data.value + 10' } },
|
||||
{ id: 'n3', type: 'condition', label: 'Check > 50', config: { condition: 'data > 50' } },
|
||||
{ id: 'n4', type: 'transform', label: 'Double', config: { transform: 'data * 2' } },
|
||||
],
|
||||
}
|
||||
const workflow = createWorkflow('w-complex', 'Complex Workflow', [
|
||||
createNode('n1', 'trigger', 'Start'),
|
||||
createNode('n2', 'transform', 'Add 10', { transform: 'data.value + 10' }),
|
||||
createNode('n3', 'condition', 'Check > 50', { condition: 'data > 50' }),
|
||||
createNode('n4', 'transform', 'Double', { transform: 'data * 2' }),
|
||||
])
|
||||
|
||||
const result = await engine.executeWorkflow(workflow, { data: { value: 45 } })
|
||||
|
||||
@@ -417,16 +332,12 @@ describe('workflow-engine', () => {
|
||||
})
|
||||
|
||||
it('should handle workflow with early termination', async () => {
|
||||
const workflow: Workflow = {
|
||||
id: 'w-early',
|
||||
name: 'Early Stop',
|
||||
nodes: [
|
||||
{ id: 'n1', type: 'trigger', label: 'Start', config: {} },
|
||||
{ id: 'n2', type: 'condition', label: 'Always False', config: { condition: 'false' } },
|
||||
{ id: 'n3', type: 'action', label: 'Never Runs', config: {} },
|
||||
{ id: 'n4', type: 'action', label: 'Also Never Runs', config: {} },
|
||||
],
|
||||
}
|
||||
const workflow = createWorkflow('w-early', 'Early Stop', [
|
||||
createNode('n1', 'trigger', 'Start'),
|
||||
createNode('n2', 'condition', 'Always False', { condition: 'false' }),
|
||||
createNode('n3', 'action', 'Never Runs'),
|
||||
createNode('n4', 'action', 'Also Never Runs'),
|
||||
])
|
||||
|
||||
const result = await engine.executeWorkflow(workflow, { data: {} })
|
||||
|
||||
|
||||
Reference in New Issue
Block a user