@pikku/core 0.12.4 → 0.12.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -1
- package/dist/dev/hot-reload.d.ts +10 -0
- package/dist/dev/hot-reload.js +158 -0
- package/dist/middleware-runner.d.ts +1 -0
- package/dist/middleware-runner.js +5 -0
- package/dist/permissions.d.ts +1 -0
- package/dist/permissions.js +5 -0
- package/dist/services/content-service.d.ts +2 -0
- package/dist/services/in-memory-workflow-service.d.ts +1 -0
- package/dist/services/in-memory-workflow-service.js +16 -11
- package/dist/services/workflow-service.d.ts +1 -0
- package/dist/wirings/ai-agent/ai-agent-prepare.js +16 -1
- package/dist/wirings/channel/channel-middleware-runner.d.ts +1 -0
- package/dist/wirings/channel/channel-middleware-runner.js +5 -0
- package/dist/wirings/http/http-runner.js +1 -1
- package/dist/wirings/workflow/pikku-workflow-service.d.ts +6 -0
- package/dist/wirings/workflow/pikku-workflow-service.js +51 -16
- package/dist/wirings/workflow/workflow.types.d.ts +2 -0
- package/package.json +5 -3
- package/src/crypto-utils.test.ts +214 -0
- package/src/crypto-utils.ts +213 -0
- package/src/dev/hot-reload.test.ts +484 -0
- package/src/dev/hot-reload.ts +212 -0
- package/src/errors/error-handler.ts +62 -0
- package/src/errors/error.test.ts +195 -0
- package/src/errors/errors.ts +374 -0
- package/src/errors/index.ts +2 -0
- package/src/factory-functions.test.ts +109 -0
- package/src/function/function-runner.test.ts +536 -0
- package/src/function/function-runner.ts +365 -0
- package/src/function/functions.types.ts +304 -0
- package/src/function/index.ts +5 -0
- package/src/handle-error.test.ts +424 -0
- package/src/handle-error.ts +69 -0
- package/src/index.ts +118 -0
- package/src/internal.ts +6 -0
- package/src/middleware/auth-apikey.test.ts +363 -0
- package/src/middleware/auth-apikey.ts +47 -0
- package/src/middleware/auth-bearer.test.ts +450 -0
- package/src/middleware/auth-bearer.ts +72 -0
- package/src/middleware/auth-cookie.test.ts +528 -0
- package/src/middleware/auth-cookie.ts +80 -0
- package/src/middleware/cors.test.ts +424 -0
- package/src/middleware/cors.ts +104 -0
- package/src/middleware/index.ts +5 -0
- package/src/middleware/remote-auth.test.ts +488 -0
- package/src/middleware/remote-auth.ts +68 -0
- package/src/middleware/timeout.ts +15 -0
- package/src/middleware-runner.test.ts +418 -0
- package/src/middleware-runner.ts +240 -0
- package/src/permissions.test.ts +434 -0
- package/src/permissions.ts +327 -0
- package/src/pikku-request.ts +23 -0
- package/src/pikku-response.ts +5 -0
- package/src/pikku-state.test.ts +224 -0
- package/src/pikku-state.ts +216 -0
- package/src/run-tests-script.test.ts +49 -0
- package/src/schema.test.ts +249 -0
- package/src/schema.ts +151 -0
- package/src/services/ai-agent-runner-service.ts +41 -0
- package/src/services/ai-run-state-service.ts +20 -0
- package/src/services/ai-storage-service.ts +39 -0
- package/src/services/content-service.ts +81 -0
- package/src/services/deployment-service.ts +22 -0
- package/src/services/gateway-service.ts +20 -0
- package/src/services/gopass-secrets.ts +98 -0
- package/src/services/in-memory-ai-run-state-service.ts +73 -0
- package/src/services/in-memory-trigger-service.ts +64 -0
- package/src/services/in-memory-workflow-service.test.ts +351 -0
- package/src/services/in-memory-workflow-service.ts +512 -0
- package/src/services/index.ts +56 -0
- package/src/services/jwt-service.ts +30 -0
- package/src/services/local-content.ts +120 -0
- package/src/services/local-gateway-service.ts +62 -0
- package/src/services/local-secrets.test.ts +80 -0
- package/src/services/local-secrets.ts +94 -0
- package/src/services/local-variables.test.ts +61 -0
- package/src/services/local-variables.ts +39 -0
- package/src/services/logger-console.test.ts +118 -0
- package/src/services/logger-console.ts +98 -0
- package/src/services/logger.ts +57 -0
- package/src/services/scheduler-service.ts +86 -0
- package/src/services/schema-service.ts +33 -0
- package/src/services/scoped-secret-service.test.ts +74 -0
- package/src/services/scoped-secret-service.ts +41 -0
- package/src/services/secret-service.ts +38 -0
- package/src/services/trigger-service.ts +17 -0
- package/src/services/typed-secret-service.test.ts +93 -0
- package/src/services/typed-secret-service.ts +70 -0
- package/src/services/typed-variables-service.test.ts +73 -0
- package/src/services/typed-variables-service.ts +81 -0
- package/src/services/user-session-service.test.ts +113 -0
- package/src/services/user-session-service.ts +86 -0
- package/src/services/variables-service.ts +11 -0
- package/src/services/workflow-service.ts +96 -0
- package/src/testing/index.ts +2 -0
- package/src/testing/service-tests.ts +873 -0
- package/src/time-utils.test.ts +56 -0
- package/src/time-utils.ts +107 -0
- package/src/types/core.types.ts +478 -0
- package/src/types/state.types.ts +188 -0
- package/src/utils/hash.test.ts +68 -0
- package/src/utils/hash.ts +26 -0
- package/src/utils.test.ts +350 -0
- package/src/utils.ts +129 -0
- package/src/version.test.ts +80 -0
- package/src/version.ts +25 -0
- package/src/wirings/ai-agent/agent-dynamic-workflow.ts +469 -0
- package/src/wirings/ai-agent/ai-agent-helpers.test.ts +152 -0
- package/src/wirings/ai-agent/ai-agent-helpers.ts +76 -0
- package/src/wirings/ai-agent/ai-agent-memory.ts +310 -0
- package/src/wirings/ai-agent/ai-agent-model-config.test.ts +115 -0
- package/src/wirings/ai-agent/ai-agent-model-config.ts +43 -0
- package/src/wirings/ai-agent/ai-agent-prepare.ts +659 -0
- package/src/wirings/ai-agent/ai-agent-registry.test.ts +37 -0
- package/src/wirings/ai-agent/ai-agent-registry.ts +82 -0
- package/src/wirings/ai-agent/ai-agent-runner.test.ts +319 -0
- package/src/wirings/ai-agent/ai-agent-runner.ts +773 -0
- package/src/wirings/ai-agent/ai-agent-stream.test.ts +859 -0
- package/src/wirings/ai-agent/ai-agent-stream.ts +1153 -0
- package/src/wirings/ai-agent/ai-agent.types.ts +382 -0
- package/src/wirings/ai-agent/index.ts +37 -0
- package/src/wirings/channel/channel-common.ts +90 -0
- package/src/wirings/channel/channel-handler.ts +250 -0
- package/src/wirings/channel/channel-middleware-runner.ts +126 -0
- package/src/wirings/channel/channel-runner.ts +166 -0
- package/src/wirings/channel/channel-store.ts +29 -0
- package/src/wirings/channel/channel.types.ts +172 -0
- package/src/wirings/channel/define-channel-routes.ts +25 -0
- package/src/wirings/channel/eventhub-service.ts +34 -0
- package/src/wirings/channel/eventhub-store.ts +13 -0
- package/src/wirings/channel/index.ts +24 -0
- package/src/wirings/channel/local/index.ts +3 -0
- package/src/wirings/channel/local/local-channel-handler.ts +81 -0
- package/src/wirings/channel/local/local-channel-runner.test.ts +215 -0
- package/src/wirings/channel/local/local-channel-runner.ts +210 -0
- package/src/wirings/channel/local/local-eventhub-service.test.ts +95 -0
- package/src/wirings/channel/local/local-eventhub-service.ts +101 -0
- package/src/wirings/channel/log-channels.ts +20 -0
- package/src/wirings/channel/pikku-abstract-channel-handler.test.ts +54 -0
- package/src/wirings/channel/pikku-abstract-channel-handler.ts +45 -0
- package/src/wirings/channel/serverless/index.ts +5 -0
- package/src/wirings/channel/serverless/serverless-channel-runner.ts +289 -0
- package/src/wirings/cli/channel/cli-channel-runner.ts +136 -0
- package/src/wirings/cli/channel/index.ts +1 -0
- package/src/wirings/cli/cli-runner.test.ts +403 -0
- package/src/wirings/cli/cli-runner.ts +529 -0
- package/src/wirings/cli/cli.types.ts +358 -0
- package/src/wirings/cli/command-parser.test.ts +445 -0
- package/src/wirings/cli/command-parser.ts +504 -0
- package/src/wirings/cli/define-cli-commands.ts +24 -0
- package/src/wirings/cli/index.ts +19 -0
- package/src/wirings/gateway/gateway-runner.test.ts +474 -0
- package/src/wirings/gateway/gateway-runner.ts +411 -0
- package/src/wirings/gateway/gateway.types.ts +149 -0
- package/src/wirings/gateway/index.ts +13 -0
- package/src/wirings/http/http-routes.test.ts +322 -0
- package/src/wirings/http/http-routes.ts +197 -0
- package/src/wirings/http/http-runner.test.ts +144 -0
- package/src/wirings/http/http-runner.ts +588 -0
- package/src/wirings/http/http.types.ts +369 -0
- package/src/wirings/http/index.ts +28 -0
- package/src/wirings/http/log-http-routes.ts +22 -0
- package/src/wirings/http/pikku-fetch-http-request.test.ts +237 -0
- package/src/wirings/http/pikku-fetch-http-request.ts +170 -0
- package/src/wirings/http/pikku-fetch-http-response.test.ts +82 -0
- package/src/wirings/http/pikku-fetch-http-response.ts +147 -0
- package/src/wirings/http/routers/http-router.ts +13 -0
- package/src/wirings/http/routers/path-to-regex.test.ts +319 -0
- package/src/wirings/http/routers/path-to-regex.ts +126 -0
- package/src/wirings/http/web-request.test.ts +236 -0
- package/src/wirings/http/web-request.ts +104 -0
- package/src/wirings/mcp/index.ts +27 -0
- package/src/wirings/mcp/mcp-endpoint-registry.test.ts +389 -0
- package/src/wirings/mcp/mcp-endpoint-registry.ts +159 -0
- package/src/wirings/mcp/mcp-runner.ts +323 -0
- package/src/wirings/mcp/mcp.types.ts +216 -0
- package/src/wirings/node/index.ts +2 -0
- package/src/wirings/node/node.types.ts +23 -0
- package/src/wirings/oauth2/index.ts +3 -0
- package/src/wirings/oauth2/oauth2-client.test.ts +929 -0
- package/src/wirings/oauth2/oauth2-client.ts +335 -0
- package/src/wirings/oauth2/oauth2.types.ts +69 -0
- package/src/wirings/oauth2/wire-oauth2-credential.ts +23 -0
- package/src/wirings/queue/index.ts +31 -0
- package/src/wirings/queue/queue-runner.test.ts +710 -0
- package/src/wirings/queue/queue-runner.ts +192 -0
- package/src/wirings/queue/queue.types.ts +189 -0
- package/src/wirings/queue/register-queue-helper.ts +60 -0
- package/src/wirings/queue/validate-worker-config.test.ts +108 -0
- package/src/wirings/queue/validate-worker-config.ts +116 -0
- package/src/wirings/rpc/index.ts +4 -0
- package/src/wirings/rpc/rpc-runner.ts +460 -0
- package/src/wirings/rpc/rpc-types.ts +56 -0
- package/src/wirings/rpc/wire-addon.ts +21 -0
- package/src/wirings/scheduler/index.ts +11 -0
- package/src/wirings/scheduler/log-schedulers.ts +20 -0
- package/src/wirings/scheduler/scheduler-runner.test.ts +660 -0
- package/src/wirings/scheduler/scheduler-runner.ts +133 -0
- package/src/wirings/scheduler/scheduler.types.ts +53 -0
- package/src/wirings/secret/index.ts +9 -0
- package/src/wirings/secret/secret.types.ts +32 -0
- package/src/wirings/secret/validate-secret-definitions.test.ts +140 -0
- package/src/wirings/secret/validate-secret-definitions.ts +82 -0
- package/src/wirings/trigger/index.ts +10 -0
- package/src/wirings/trigger/pikku-trigger-service.ts +112 -0
- package/src/wirings/trigger/trigger-runner.test.ts +79 -0
- package/src/wirings/trigger/trigger-runner.ts +135 -0
- package/src/wirings/trigger/trigger.types.ts +178 -0
- package/src/wirings/variable/index.ts +8 -0
- package/src/wirings/variable/validate-variable-definitions.test.ts +91 -0
- package/src/wirings/variable/validate-variable-definitions.ts +69 -0
- package/src/wirings/variable/variable.types.ts +22 -0
- package/src/wirings/workflow/dsl/index.ts +31 -0
- package/src/wirings/workflow/dsl/workflow-dsl.types.ts +320 -0
- package/src/wirings/workflow/dsl/workflow-runner.ts +28 -0
- package/src/wirings/workflow/graph/graph-node.ts +222 -0
- package/src/wirings/workflow/graph/graph-runner.test.ts +487 -0
- package/src/wirings/workflow/graph/graph-runner.ts +816 -0
- package/src/wirings/workflow/graph/graph-validation.test.ts +170 -0
- package/src/wirings/workflow/graph/graph-validation.ts +237 -0
- package/src/wirings/workflow/graph/index.ts +19 -0
- package/src/wirings/workflow/graph/template.test.ts +49 -0
- package/src/wirings/workflow/graph/template.ts +42 -0
- package/src/wirings/workflow/graph/wire-workflow-graph.ts +29 -0
- package/src/wirings/workflow/graph/workflow-graph.types.ts +104 -0
- package/src/wirings/workflow/index.ts +82 -0
- package/src/wirings/workflow/pikku-workflow-service.test.ts +200 -0
- package/src/wirings/workflow/pikku-workflow-service.ts +1229 -0
- package/src/wirings/workflow/workflow-helpers.test.ts +129 -0
- package/src/wirings/workflow/workflow-helpers.ts +79 -0
- package/src/wirings/workflow/workflow.types.ts +276 -0
- package/tsconfig.json +14 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/lcov.info +0 -18891
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
import { describe, test } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
|
|
4
|
+
import { InMemoryWorkflowService } from '../../../services/in-memory-workflow-service.js'
|
|
5
|
+
import {
|
|
6
|
+
continueGraph,
|
|
7
|
+
executeGraphStep,
|
|
8
|
+
runWorkflowGraph,
|
|
9
|
+
} from './graph-runner.js'
|
|
10
|
+
import type { WorkflowRuntimeMeta } from '../workflow.types.js'
|
|
11
|
+
import { pikkuState } from '../../../pikku-state.js'
|
|
12
|
+
import { RPCNotFoundError } from '../../rpc/rpc-runner.js'
|
|
13
|
+
|
|
14
|
+
describe('graph-runner bugs', () => {
|
|
15
|
+
test('continueGraph should NOT mark workflow completed while nodes are still running', async () => {
|
|
16
|
+
const ws = new InMemoryWorkflowService()
|
|
17
|
+
|
|
18
|
+
const meta: WorkflowRuntimeMeta = {
|
|
19
|
+
name: 'testPrematureCompletion',
|
|
20
|
+
pikkuFuncId: 'testPrematureCompletion',
|
|
21
|
+
source: 'graph',
|
|
22
|
+
entryNodeIds: ['a'],
|
|
23
|
+
graphHash: 'test-hash',
|
|
24
|
+
nodes: {
|
|
25
|
+
a: { nodeId: 'a', rpcName: 'doA', next: 'b' },
|
|
26
|
+
b: { nodeId: 'b', rpcName: 'doB' },
|
|
27
|
+
},
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const runId = await ws.createRun(
|
|
31
|
+
'testPrematureCompletion',
|
|
32
|
+
{},
|
|
33
|
+
false,
|
|
34
|
+
'test-hash',
|
|
35
|
+
{ type: 'test' }
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
const stepA = await ws.insertStepState(runId, 'a', 'doA', {})
|
|
39
|
+
await ws.setStepRunning(stepA.stepId)
|
|
40
|
+
await ws.setStepResult(stepA.stepId, { ok: true })
|
|
41
|
+
|
|
42
|
+
const stepB = await ws.insertStepState(runId, 'b', 'doB', {})
|
|
43
|
+
await ws.setStepRunning(stepB.stepId)
|
|
44
|
+
|
|
45
|
+
await continueGraph(ws, runId, 'testPrematureCompletion', meta)
|
|
46
|
+
|
|
47
|
+
const run = await ws.getRun(runId)
|
|
48
|
+
assert.notEqual(
|
|
49
|
+
run?.status,
|
|
50
|
+
'completed',
|
|
51
|
+
'workflow should NOT be completed while node b is still running'
|
|
52
|
+
)
|
|
53
|
+
assert.equal(run?.status, 'running')
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
test('runWorkflowGraph should throw and not create a run for unknown input refs', async () => {
|
|
57
|
+
const ws = new InMemoryWorkflowService()
|
|
58
|
+
|
|
59
|
+
const metaState = pikkuState(null, 'workflows', 'meta')
|
|
60
|
+
metaState['testOrphanedRun'] = {
|
|
61
|
+
name: 'testOrphanedRun',
|
|
62
|
+
pikkuFuncId: 'testOrphanedRun',
|
|
63
|
+
source: 'graph',
|
|
64
|
+
entryNodeIds: ['a'],
|
|
65
|
+
graphHash: 'orphan-hash',
|
|
66
|
+
nodes: {
|
|
67
|
+
a: {
|
|
68
|
+
nodeId: 'a',
|
|
69
|
+
rpcName: 'doA',
|
|
70
|
+
input: { dep: { $ref: 'someOtherNode', path: 'value' } },
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
await assert.rejects(() => runWorkflowGraph(ws, 'testOrphanedRun', {}), {
|
|
76
|
+
message: /references unknown node 'someOtherNode' in input/,
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
const runs: string[] = [...(ws as any).runs.keys()]
|
|
80
|
+
assert.equal(
|
|
81
|
+
runs.length,
|
|
82
|
+
0,
|
|
83
|
+
'no run should be created when entry nodes have unsatisfied dependencies'
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
delete metaState['testOrphanedRun']
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
test('runWorkflowGraph should throw and not create a run for unknown next targets', async () => {
|
|
90
|
+
const ws = new InMemoryWorkflowService()
|
|
91
|
+
|
|
92
|
+
const metaState = pikkuState(null, 'workflows', 'meta')
|
|
93
|
+
metaState['testUnknownNextTarget'] = {
|
|
94
|
+
name: 'testUnknownNextTarget',
|
|
95
|
+
pikkuFuncId: 'testUnknownNextTarget',
|
|
96
|
+
source: 'graph',
|
|
97
|
+
entryNodeIds: ['a'],
|
|
98
|
+
graphHash: 'unknown-next-hash',
|
|
99
|
+
nodes: {
|
|
100
|
+
a: { nodeId: 'a', rpcName: 'doA', next: 'missingNode' },
|
|
101
|
+
},
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
await assert.rejects(
|
|
105
|
+
() => runWorkflowGraph(ws, 'testUnknownNextTarget', {}),
|
|
106
|
+
{
|
|
107
|
+
message: /routes to unknown node 'missingNode'/,
|
|
108
|
+
}
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
const runs: string[] = [...(ws as any).runs.keys()]
|
|
112
|
+
assert.equal(
|
|
113
|
+
runs.length,
|
|
114
|
+
0,
|
|
115
|
+
'no run should be created for invalid next targets'
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
delete metaState['testUnknownNextTarget']
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
test('continueGraph should throw for ambiguous template step remapping', async () => {
|
|
122
|
+
const ws = new InMemoryWorkflowService()
|
|
123
|
+
|
|
124
|
+
const meta: WorkflowRuntimeMeta = {
|
|
125
|
+
name: 'testAmbiguousTemplateStepRemap',
|
|
126
|
+
pikkuFuncId: 'testAmbiguousTemplateStepRemap',
|
|
127
|
+
source: 'graph',
|
|
128
|
+
entryNodeIds: ['start'],
|
|
129
|
+
graphHash: 'ambiguous-step-remap-hash',
|
|
130
|
+
nodes: {
|
|
131
|
+
start: { nodeId: 'start', rpcName: 'doStart' },
|
|
132
|
+
'task-${id}': { nodeId: 'task-${id}', rpcName: 'doTaskById' },
|
|
133
|
+
'task-${name}': { nodeId: 'task-${name}', rpcName: 'doTaskByName' },
|
|
134
|
+
},
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const runId = await ws.createRun(
|
|
138
|
+
'testAmbiguousTemplateStepRemap',
|
|
139
|
+
{},
|
|
140
|
+
false,
|
|
141
|
+
'ambiguous-step-remap-hash',
|
|
142
|
+
{ type: 'test' }
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
const step = await ws.insertStepState(runId, 'task-123', 'doTask', {})
|
|
146
|
+
await ws.setStepRunning(step.stepId)
|
|
147
|
+
await ws.setStepResult(step.stepId, { ok: true })
|
|
148
|
+
|
|
149
|
+
await assert.rejects(
|
|
150
|
+
() => continueGraph(ws, runId, 'testAmbiguousTemplateStepRemap', meta),
|
|
151
|
+
{
|
|
152
|
+
message: /ambiguous template node match for 'task-123'/i,
|
|
153
|
+
}
|
|
154
|
+
)
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
test('continueGraph should throw for ambiguous template branch key remapping', async () => {
|
|
158
|
+
const ws = new InMemoryWorkflowService()
|
|
159
|
+
|
|
160
|
+
const meta: WorkflowRuntimeMeta = {
|
|
161
|
+
name: 'testAmbiguousTemplateBranchRemap',
|
|
162
|
+
pikkuFuncId: 'testAmbiguousTemplateBranchRemap',
|
|
163
|
+
source: 'graph',
|
|
164
|
+
entryNodeIds: ['start'],
|
|
165
|
+
graphHash: 'ambiguous-branch-remap-hash',
|
|
166
|
+
nodes: {
|
|
167
|
+
start: { nodeId: 'start', rpcName: 'doStart' },
|
|
168
|
+
'branch-${id}': { nodeId: 'branch-${id}', rpcName: 'doBranchById' },
|
|
169
|
+
'branch-${name}': {
|
|
170
|
+
nodeId: 'branch-${name}',
|
|
171
|
+
rpcName: 'doBranchByName',
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const runId = await ws.createRun(
|
|
177
|
+
'testAmbiguousTemplateBranchRemap',
|
|
178
|
+
{},
|
|
179
|
+
false,
|
|
180
|
+
'ambiguous-branch-remap-hash',
|
|
181
|
+
{ type: 'test' }
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
const step = await ws.insertStepState(runId, 'branch-1', 'doBranch', {})
|
|
185
|
+
await ws.setBranchTaken(step.stepId, 'next')
|
|
186
|
+
|
|
187
|
+
await assert.rejects(
|
|
188
|
+
() => continueGraph(ws, runId, 'testAmbiguousTemplateBranchRemap', meta),
|
|
189
|
+
{
|
|
190
|
+
message: /ambiguous template branch key match for 'branch-1'/i,
|
|
191
|
+
}
|
|
192
|
+
)
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
test('continueGraph should queue converging next node only once', async () => {
|
|
196
|
+
const ws = new InMemoryWorkflowService()
|
|
197
|
+
const queuedNodes: string[] = []
|
|
198
|
+
|
|
199
|
+
pikkuState(null, 'package', 'singletonServices', {
|
|
200
|
+
queueService: {
|
|
201
|
+
add: async (_queueName: string, data: any) => {
|
|
202
|
+
if (data?.stepName) {
|
|
203
|
+
queuedNodes.push(data.stepName)
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
} as any)
|
|
208
|
+
|
|
209
|
+
const meta: WorkflowRuntimeMeta = {
|
|
210
|
+
name: 'testConvergingNextNode',
|
|
211
|
+
pikkuFuncId: 'testConvergingNextNode',
|
|
212
|
+
source: 'graph',
|
|
213
|
+
entryNodeIds: ['a', 'b'],
|
|
214
|
+
graphHash: 'converging-hash',
|
|
215
|
+
nodes: {
|
|
216
|
+
a: { nodeId: 'a', rpcName: 'doA', next: 'c' },
|
|
217
|
+
b: { nodeId: 'b', rpcName: 'doB', next: 'c' },
|
|
218
|
+
c: { nodeId: 'c', rpcName: 'doC' },
|
|
219
|
+
},
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const runId = await ws.createRun(
|
|
223
|
+
'testConvergingNextNode',
|
|
224
|
+
{},
|
|
225
|
+
false,
|
|
226
|
+
'converging-hash',
|
|
227
|
+
{ type: 'test' }
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
const stepA = await ws.insertStepState(runId, 'a', 'doA', {})
|
|
231
|
+
await ws.setStepRunning(stepA.stepId)
|
|
232
|
+
await ws.setStepResult(stepA.stepId, { ok: true })
|
|
233
|
+
|
|
234
|
+
const stepB = await ws.insertStepState(runId, 'b', 'doB', {})
|
|
235
|
+
await ws.setStepRunning(stepB.stepId)
|
|
236
|
+
await ws.setStepResult(stepB.stepId, { ok: true })
|
|
237
|
+
|
|
238
|
+
await continueGraph(ws, runId, 'testConvergingNextNode', meta)
|
|
239
|
+
|
|
240
|
+
assert.deepEqual(queuedNodes, ['c'])
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
test('inline graph should execute converging next node only once', async () => {
|
|
244
|
+
const ws = new InMemoryWorkflowService()
|
|
245
|
+
let cCalls = 0
|
|
246
|
+
|
|
247
|
+
const mockRpcService = {
|
|
248
|
+
rpcWithWire: async (rpcName: string) => {
|
|
249
|
+
if (rpcName === 'doA' || rpcName === 'doB') {
|
|
250
|
+
return { ok: true }
|
|
251
|
+
}
|
|
252
|
+
if (rpcName === 'doC') {
|
|
253
|
+
cCalls += 1
|
|
254
|
+
return { ok: true }
|
|
255
|
+
}
|
|
256
|
+
return {}
|
|
257
|
+
},
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const metaState = pikkuState(null, 'workflows', 'meta')
|
|
261
|
+
metaState['testInlineConvergingSingleExecution'] = {
|
|
262
|
+
name: 'testInlineConvergingSingleExecution',
|
|
263
|
+
pikkuFuncId: 'testInlineConvergingSingleExecution',
|
|
264
|
+
source: 'graph',
|
|
265
|
+
entryNodeIds: ['a', 'b'],
|
|
266
|
+
graphHash: 'inline-converging-hash',
|
|
267
|
+
nodes: {
|
|
268
|
+
a: { nodeId: 'a', rpcName: 'doA', next: 'c' },
|
|
269
|
+
b: { nodeId: 'b', rpcName: 'doB', next: 'c' },
|
|
270
|
+
c: { nodeId: 'c', rpcName: 'doC' },
|
|
271
|
+
},
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
await runWorkflowGraph(
|
|
275
|
+
ws,
|
|
276
|
+
'testInlineConvergingSingleExecution',
|
|
277
|
+
{},
|
|
278
|
+
mockRpcService,
|
|
279
|
+
true
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
assert.equal(cCalls, 1)
|
|
283
|
+
|
|
284
|
+
delete metaState['testInlineConvergingSingleExecution']
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
test('inline graph node failure should mark workflow as failed', async () => {
|
|
288
|
+
const ws = new InMemoryWorkflowService()
|
|
289
|
+
|
|
290
|
+
const mockRpcService = {
|
|
291
|
+
rpcWithWire: async (rpcName: string, _data: any, _wire: any) => {
|
|
292
|
+
if (rpcName === 'doA') return { ok: true }
|
|
293
|
+
if (rpcName === 'doB') throw new Error('doB exploded')
|
|
294
|
+
return {}
|
|
295
|
+
},
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const metaState = pikkuState(null, 'workflows', 'meta')
|
|
299
|
+
metaState['testInlineFailure'] = {
|
|
300
|
+
name: 'testInlineFailure',
|
|
301
|
+
pikkuFuncId: 'testInlineFailure',
|
|
302
|
+
source: 'graph',
|
|
303
|
+
entryNodeIds: ['a'],
|
|
304
|
+
graphHash: 'inline-fail-hash',
|
|
305
|
+
nodes: {
|
|
306
|
+
a: { nodeId: 'a', rpcName: 'doA', next: 'b' },
|
|
307
|
+
b: { nodeId: 'b', rpcName: 'doB' },
|
|
308
|
+
},
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
let runId: string | undefined
|
|
312
|
+
try {
|
|
313
|
+
const result = await runWorkflowGraph(
|
|
314
|
+
ws,
|
|
315
|
+
'testInlineFailure',
|
|
316
|
+
{},
|
|
317
|
+
mockRpcService,
|
|
318
|
+
true
|
|
319
|
+
)
|
|
320
|
+
runId = result.runId
|
|
321
|
+
} catch {
|
|
322
|
+
const runs = (ws as any).runs as Map<string, any>
|
|
323
|
+
for (const [id] of runs) {
|
|
324
|
+
runId = id
|
|
325
|
+
break
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
assert.ok(runId, 'should have created a run')
|
|
330
|
+
const run = await ws.getRun(runId!)
|
|
331
|
+
assert.equal(
|
|
332
|
+
run?.status,
|
|
333
|
+
'failed',
|
|
334
|
+
'workflow should be failed after inline node error, not left running'
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
delete metaState['testInlineFailure']
|
|
338
|
+
})
|
|
339
|
+
|
|
340
|
+
test('executeGraphStep should throw after queueing onError nodes', async () => {
|
|
341
|
+
const ws = new InMemoryWorkflowService()
|
|
342
|
+
|
|
343
|
+
pikkuState(null, 'package', 'singletonServices', {
|
|
344
|
+
queueService: {
|
|
345
|
+
add: async () => {},
|
|
346
|
+
},
|
|
347
|
+
} as any)
|
|
348
|
+
|
|
349
|
+
const metaState = pikkuState(null, 'workflows', 'meta')
|
|
350
|
+
metaState['testQueuedOnErrorThrow'] = {
|
|
351
|
+
name: 'testQueuedOnErrorThrow',
|
|
352
|
+
pikkuFuncId: 'testQueuedOnErrorThrow',
|
|
353
|
+
source: 'graph',
|
|
354
|
+
entryNodeIds: ['a'],
|
|
355
|
+
graphHash: 'queued-on-error-hash',
|
|
356
|
+
nodes: {
|
|
357
|
+
a: { nodeId: 'a', rpcName: 'doA', onError: 'onErr' },
|
|
358
|
+
onErr: { nodeId: 'onErr', rpcName: 'handleErr' },
|
|
359
|
+
},
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
const runId = await ws.createRun(
|
|
363
|
+
'testQueuedOnErrorThrow',
|
|
364
|
+
{},
|
|
365
|
+
false,
|
|
366
|
+
'queued-on-error-hash',
|
|
367
|
+
{ type: 'test' }
|
|
368
|
+
)
|
|
369
|
+
const step = await ws.insertStepState(runId, 'a', 'doA', {})
|
|
370
|
+
|
|
371
|
+
const rpcService = {
|
|
372
|
+
rpcWithWire: async () => {
|
|
373
|
+
throw new Error('boom')
|
|
374
|
+
},
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
await assert.rejects(
|
|
378
|
+
() =>
|
|
379
|
+
executeGraphStep(
|
|
380
|
+
ws,
|
|
381
|
+
rpcService,
|
|
382
|
+
runId,
|
|
383
|
+
step.stepId,
|
|
384
|
+
'a',
|
|
385
|
+
'doA',
|
|
386
|
+
{},
|
|
387
|
+
'testQueuedOnErrorThrow'
|
|
388
|
+
),
|
|
389
|
+
/boom/
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
const errorStep = await ws.getStepState(runId, 'onErr')
|
|
393
|
+
assert.notEqual(
|
|
394
|
+
errorStep.stepId,
|
|
395
|
+
'',
|
|
396
|
+
'onError node should be queued as a step'
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
delete metaState['testQueuedOnErrorThrow']
|
|
400
|
+
})
|
|
401
|
+
|
|
402
|
+
test('executeWorkflowStep should mark graph step failed and suspend run on RPCNotFoundError', async () => {
|
|
403
|
+
const ws = new InMemoryWorkflowService()
|
|
404
|
+
|
|
405
|
+
const metaState = pikkuState(null, 'workflows', 'meta')
|
|
406
|
+
metaState['testQueuedRpcMissing'] = {
|
|
407
|
+
name: 'testQueuedRpcMissing',
|
|
408
|
+
pikkuFuncId: 'testQueuedRpcMissing',
|
|
409
|
+
source: 'graph',
|
|
410
|
+
entryNodeIds: ['a'],
|
|
411
|
+
graphHash: 'queued-rpc-missing-hash',
|
|
412
|
+
nodes: {
|
|
413
|
+
a: { nodeId: 'a', rpcName: 'missingRpc' },
|
|
414
|
+
},
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
const runId = await ws.createRun(
|
|
418
|
+
'testQueuedRpcMissing',
|
|
419
|
+
{},
|
|
420
|
+
false,
|
|
421
|
+
'queued-rpc-missing-hash',
|
|
422
|
+
{ type: 'test' }
|
|
423
|
+
)
|
|
424
|
+
await ws.insertStepState(runId, 'a', 'missingRpc', {})
|
|
425
|
+
|
|
426
|
+
const rpcService = {
|
|
427
|
+
rpcWithWire: async () => {
|
|
428
|
+
throw new RPCNotFoundError('missingRpc')
|
|
429
|
+
},
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
await ws.executeWorkflowStep(runId, 'a', 'missingRpc', {}, rpcService)
|
|
433
|
+
|
|
434
|
+
const step = await ws.getStepState(runId, 'a')
|
|
435
|
+
const run = await ws.getRun(runId)
|
|
436
|
+
|
|
437
|
+
assert.equal(step.status, 'failed')
|
|
438
|
+
assert.equal(step.error?.message, 'RPC function not found: missingRpc')
|
|
439
|
+
assert.equal(run?.status, 'suspended')
|
|
440
|
+
assert.equal(run?.error?.code, 'RPC_NOT_FOUND')
|
|
441
|
+
|
|
442
|
+
delete metaState['testQueuedRpcMissing']
|
|
443
|
+
})
|
|
444
|
+
|
|
445
|
+
test('inline graph missing RPC should mark step failed and suspend run', async () => {
|
|
446
|
+
const ws = new InMemoryWorkflowService()
|
|
447
|
+
|
|
448
|
+
const metaState = pikkuState(null, 'workflows', 'meta')
|
|
449
|
+
metaState['testInlineRpcMissing'] = {
|
|
450
|
+
name: 'testInlineRpcMissing',
|
|
451
|
+
pikkuFuncId: 'testInlineRpcMissing',
|
|
452
|
+
source: 'graph',
|
|
453
|
+
entryNodeIds: ['a'],
|
|
454
|
+
graphHash: 'inline-rpc-missing-hash',
|
|
455
|
+
nodes: {
|
|
456
|
+
a: { nodeId: 'a', rpcName: 'missingInlineRpc' },
|
|
457
|
+
},
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
const rpcService = {
|
|
461
|
+
rpcWithWire: async () => {
|
|
462
|
+
throw new RPCNotFoundError('missingInlineRpc')
|
|
463
|
+
},
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
const { runId } = await runWorkflowGraph(
|
|
467
|
+
ws,
|
|
468
|
+
'testInlineRpcMissing',
|
|
469
|
+
{},
|
|
470
|
+
rpcService,
|
|
471
|
+
true
|
|
472
|
+
)
|
|
473
|
+
|
|
474
|
+
const step = await ws.getStepState(runId, 'a')
|
|
475
|
+
const run = await ws.getRun(runId)
|
|
476
|
+
|
|
477
|
+
assert.equal(step.status, 'failed')
|
|
478
|
+
assert.equal(
|
|
479
|
+
step.error?.message,
|
|
480
|
+
'RPC function not found: missingInlineRpc'
|
|
481
|
+
)
|
|
482
|
+
assert.equal(run?.status, 'suspended')
|
|
483
|
+
assert.equal(run?.error?.code, 'RPC_NOT_FOUND')
|
|
484
|
+
|
|
485
|
+
delete metaState['testInlineRpcMissing']
|
|
486
|
+
})
|
|
487
|
+
})
|