keystone-cli 1.0.3 → 1.1.1
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/README.md +276 -32
- package/package.json +8 -4
- package/src/cli.ts +350 -416
- package/src/commands/doc.ts +31 -0
- package/src/commands/event.ts +29 -0
- package/src/commands/graph.ts +37 -0
- package/src/commands/index.ts +14 -0
- package/src/commands/init.ts +185 -0
- package/src/commands/run.ts +124 -0
- package/src/commands/schema.ts +40 -0
- package/src/commands/utils.ts +78 -0
- package/src/commands/validate.ts +111 -0
- package/src/db/workflow-db.test.ts +314 -0
- package/src/db/workflow-db.ts +810 -210
- package/src/expression/evaluator-audit.test.ts +4 -2
- package/src/expression/evaluator.test.ts +14 -1
- package/src/expression/evaluator.ts +166 -19
- package/src/parser/config-schema.ts +18 -0
- package/src/parser/schema.ts +153 -22
- package/src/parser/test-schema.ts +6 -6
- package/src/parser/workflow-parser.test.ts +24 -0
- package/src/parser/workflow-parser.ts +65 -3
- package/src/runner/auto-heal.test.ts +5 -6
- package/src/runner/blueprint-executor.test.ts +2 -2
- package/src/runner/debug-repl.test.ts +5 -8
- package/src/runner/debug-repl.ts +59 -16
- package/src/runner/durable-timers.test.ts +11 -2
- package/src/runner/engine-executor.test.ts +1 -1
- package/src/runner/events.ts +57 -0
- package/src/runner/executors/artifact-executor.ts +166 -0
- package/src/runner/{blueprint-executor.ts → executors/blueprint-executor.ts} +15 -7
- package/src/runner/{engine-executor.ts → executors/engine-executor.ts} +55 -7
- package/src/runner/executors/file-executor.test.ts +48 -0
- package/src/runner/executors/file-executor.ts +324 -0
- package/src/runner/{foreach-executor.ts → executors/foreach-executor.ts} +168 -80
- package/src/runner/executors/human-executor.ts +144 -0
- package/src/runner/executors/join-executor.ts +75 -0
- package/src/runner/executors/llm-executor.ts +1266 -0
- package/src/runner/executors/memory-executor.ts +71 -0
- package/src/runner/executors/plan-executor.ts +104 -0
- package/src/runner/executors/request-executor.ts +265 -0
- package/src/runner/executors/script-executor.ts +43 -0
- package/src/runner/executors/shell-executor.ts +403 -0
- package/src/runner/executors/subworkflow-executor.ts +114 -0
- package/src/runner/executors/types.ts +69 -0
- package/src/runner/executors/wait-executor.ts +59 -0
- package/src/runner/join-scheduling.test.ts +197 -0
- package/src/runner/llm-adapter-runtime.test.ts +209 -0
- package/src/runner/llm-adapter.test.ts +419 -24
- package/src/runner/llm-adapter.ts +130 -26
- package/src/runner/llm-clarification.test.ts +2 -1
- package/src/runner/llm-executor.test.ts +532 -17
- package/src/runner/mcp-client-audit.test.ts +1 -2
- package/src/runner/mcp-client.ts +136 -46
- package/src/runner/mcp-manager.test.ts +4 -0
- package/src/runner/mcp-server.test.ts +58 -0
- package/src/runner/mcp-server.ts +26 -0
- package/src/runner/memoization.test.ts +190 -0
- package/src/runner/optimization-runner.ts +4 -9
- package/src/runner/quality-gate.test.ts +69 -0
- package/src/runner/reflexion.test.ts +6 -17
- package/src/runner/resource-pool.ts +102 -14
- package/src/runner/services/context-builder.ts +144 -0
- package/src/runner/services/secret-manager.ts +105 -0
- package/src/runner/services/workflow-validator.ts +131 -0
- package/src/runner/shell-executor.test.ts +28 -4
- package/src/runner/standard-tools-ast.test.ts +196 -0
- package/src/runner/standard-tools-execution.test.ts +27 -0
- package/src/runner/standard-tools-integration.test.ts +6 -10
- package/src/runner/standard-tools.ts +339 -102
- package/src/runner/step-executor.test.ts +216 -4
- package/src/runner/step-executor.ts +69 -941
- package/src/runner/stream-utils.ts +7 -3
- package/src/runner/test-harness.ts +20 -1
- package/src/runner/timeout.test.ts +10 -0
- package/src/runner/timeout.ts +11 -2
- package/src/runner/tool-integration.test.ts +1 -1
- package/src/runner/wait-step.test.ts +102 -0
- package/src/runner/workflow-runner.test.ts +208 -15
- package/src/runner/workflow-runner.ts +890 -818
- package/src/runner/workflow-scheduler.ts +75 -0
- package/src/runner/workflow-state.ts +269 -0
- package/src/runner/workflow-subflows.test.ts +13 -12
- package/src/scripts/generate-schemas.ts +16 -0
- package/src/templates/agents/explore.md +1 -0
- package/src/templates/agents/general.md +1 -0
- package/src/templates/agents/handoff-router.md +14 -0
- package/src/templates/agents/handoff-specialist.md +15 -0
- package/src/templates/agents/keystone-architect.md +13 -44
- package/src/templates/agents/my-agent.md +1 -0
- package/src/templates/agents/software-engineer.md +1 -0
- package/src/templates/agents/summarizer.md +1 -0
- package/src/templates/agents/test-agent.md +1 -0
- package/src/templates/agents/tester.md +1 -0
- package/src/templates/{basic-inputs.yaml → basics/basic-inputs.yaml} +2 -0
- package/src/templates/{basic-shell.yaml → basics/basic-shell.yaml} +4 -1
- package/src/templates/{full-feature-demo.yaml → basics/full-feature-demo.yaml} +2 -0
- package/src/templates/{stop-watch.yaml → basics/stop-watch.yaml} +1 -0
- package/src/templates/{child-rollback.yaml → control-flow/child-rollback.yaml} +1 -0
- package/src/templates/{cleanup-finally.yaml → control-flow/cleanup-finally.yaml} +1 -0
- package/src/templates/{fan-out-fan-in.yaml → control-flow/fan-out-fan-in.yaml} +3 -0
- package/src/templates/control-flow/idempotency-example.yaml +30 -0
- package/src/templates/{loop-parallel.yaml → control-flow/loop-parallel.yaml} +3 -0
- package/src/templates/{parent-rollback.yaml → control-flow/parent-rollback.yaml} +1 -0
- package/src/templates/{retry-policy.yaml → control-flow/retry-policy.yaml} +3 -0
- package/src/templates/features/artifact-example.yaml +40 -0
- package/src/templates/{engine-example.yaml → features/engine-example.yaml} +1 -0
- package/src/templates/{human-interaction.yaml → features/human-interaction.yaml} +1 -0
- package/src/templates/{llm-agent.yaml → features/llm-agent.yaml} +1 -0
- package/src/templates/{memory-service.yaml → features/memory-service.yaml} +2 -0
- package/src/templates/{robust-automation.yaml → features/robust-automation.yaml} +3 -0
- package/src/templates/features/script-example.yaml +28 -0
- package/src/templates/patterns/agent-handoff.yaml +53 -0
- package/src/templates/{approval-process.yaml → patterns/approval-process.yaml} +1 -0
- package/src/templates/{batch-processor.yaml → patterns/batch-processor.yaml} +2 -0
- package/src/templates/{composition-child.yaml → patterns/composition-child.yaml} +2 -1
- package/src/templates/patterns/composition-parent.yaml +18 -0
- package/src/templates/{data-pipeline.yaml → patterns/data-pipeline.yaml} +2 -0
- package/src/templates/{decompose-implement.yaml → scaffolding/decompose-implement.yaml} +1 -0
- package/src/templates/{decompose-problem.yaml → scaffolding/decompose-problem.yaml} +1 -0
- package/src/templates/{decompose-research.yaml → scaffolding/decompose-research.yaml} +1 -0
- package/src/templates/{decompose-review.yaml → scaffolding/decompose-review.yaml} +1 -0
- package/src/templates/{dev.yaml → scaffolding/dev.yaml} +1 -0
- package/src/templates/scaffolding/review-loop.yaml +97 -0
- package/src/templates/{scaffold-feature.yaml → scaffolding/scaffold-feature.yaml} +2 -0
- package/src/templates/{scaffold-generate.yaml → scaffolding/scaffold-generate.yaml} +1 -0
- package/src/templates/{scaffold-plan.yaml → scaffolding/scaffold-plan.yaml} +1 -0
- package/src/templates/testing/invalid.yaml +6 -0
- package/src/ui/dashboard.tsx +191 -33
- package/src/utils/auth-manager.test.ts +337 -0
- package/src/utils/auth-manager.ts +157 -61
- package/src/utils/blueprint-utils.ts +4 -6
- package/src/utils/config-loader.test.ts +2 -0
- package/src/utils/config-loader.ts +12 -3
- package/src/utils/constants.ts +76 -0
- package/src/utils/container.ts +63 -0
- package/src/utils/context-injector.test.ts +200 -0
- package/src/utils/context-injector.ts +244 -0
- package/src/utils/doc-generator.ts +85 -0
- package/src/utils/env-filter.ts +45 -0
- package/src/utils/json-parser.test.ts +12 -0
- package/src/utils/json-parser.ts +30 -5
- package/src/utils/logger.ts +12 -1
- package/src/utils/mermaid.ts +4 -0
- package/src/utils/paths.ts +52 -1
- package/src/utils/process-sandbox-worker.test.ts +46 -0
- package/src/utils/process-sandbox.ts +227 -14
- package/src/utils/redactor.test.ts +11 -6
- package/src/utils/redactor.ts +25 -9
- package/src/utils/sandbox.ts +3 -0
- package/src/runner/llm-executor.ts +0 -638
- package/src/runner/shell-executor.ts +0 -366
- package/src/templates/composition-parent.yaml +0 -14
- package/src/templates/invalid.yaml +0 -5
|
@@ -151,4 +151,318 @@ describe('WorkflowDb', () => {
|
|
|
151
151
|
expect(limitedOne).toHaveLength(1);
|
|
152
152
|
expect(limitedOne[0].id).toBe('run-s4');
|
|
153
153
|
});
|
|
154
|
+
|
|
155
|
+
describe('Transactions', () => {
|
|
156
|
+
it('should commit successful transactions', () => {
|
|
157
|
+
db.withTransaction((sqlite) => {
|
|
158
|
+
sqlite.exec(
|
|
159
|
+
"INSERT INTO workflow_runs (id, workflow_name, status, inputs, started_at) VALUES ('tx-1', 'wf', 'pending', '{}', 'now')"
|
|
160
|
+
);
|
|
161
|
+
});
|
|
162
|
+
// @ts-ignore
|
|
163
|
+
const run = db.db.query("SELECT * FROM workflow_runs WHERE id = 'tx-1'").get();
|
|
164
|
+
expect(run).toBeDefined();
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('should rollback failed transactions', () => {
|
|
168
|
+
try {
|
|
169
|
+
db.withTransaction((sqlite) => {
|
|
170
|
+
sqlite.exec(
|
|
171
|
+
"INSERT INTO workflow_runs (id, workflow_name, status, inputs, started_at) VALUES ('tx-2', 'wf', 'pending', '{}', 'now')"
|
|
172
|
+
);
|
|
173
|
+
throw new Error('rollback');
|
|
174
|
+
});
|
|
175
|
+
} catch (e) {
|
|
176
|
+
// expected
|
|
177
|
+
}
|
|
178
|
+
// @ts-ignore
|
|
179
|
+
const run = db.db.query("SELECT * FROM workflow_runs WHERE id = 'tx-2'").get();
|
|
180
|
+
expect(run).toBeNull();
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
describe('Idempotency', () => {
|
|
185
|
+
it('should store and retrieve idempotency records', async () => {
|
|
186
|
+
const runId = 'run-idemp';
|
|
187
|
+
await db.createRun(runId, 'wf', {});
|
|
188
|
+
|
|
189
|
+
const success = await db.insertIdempotencyRecordIfAbsent('key-1', runId, 'step-1', 'pending');
|
|
190
|
+
expect(success).toBe(true);
|
|
191
|
+
|
|
192
|
+
const record = await db.getIdempotencyRecord('key-1');
|
|
193
|
+
expect(record?.idempotency_key).toBe('key-1');
|
|
194
|
+
|
|
195
|
+
const fail = await db.insertIdempotencyRecordIfAbsent('key-1', runId, 'step-1', 'pending');
|
|
196
|
+
expect(fail).toBe(false);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('should mark record as running', async () => {
|
|
200
|
+
const runId = 'run-idemp-2';
|
|
201
|
+
await db.createRun(runId, 'wf', {});
|
|
202
|
+
await db.insertIdempotencyRecordIfAbsent('key-2', runId, 'step-2', 'pending');
|
|
203
|
+
|
|
204
|
+
const success = await db.markIdempotencyRecordRunning('key-2', runId, 'step-2');
|
|
205
|
+
expect(success).toBe(true);
|
|
206
|
+
|
|
207
|
+
const record = await db.getIdempotencyRecord('key-2');
|
|
208
|
+
expect(record?.status).toBe('running');
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it('should clear idempotency records by run', async () => {
|
|
212
|
+
const runId = 'run-to-clear';
|
|
213
|
+
await db.createRun(runId, 'wf', {});
|
|
214
|
+
await db.insertIdempotencyRecordIfAbsent('clear-key', runId, 'step-1', 'success');
|
|
215
|
+
|
|
216
|
+
await db.clearIdempotencyRecords(runId);
|
|
217
|
+
const record = await db.getIdempotencyRecord('clear-key');
|
|
218
|
+
expect(record).toBeNull();
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it('should clear expired idempotency records', async () => {
|
|
222
|
+
const runId = 'run-expires';
|
|
223
|
+
await db.createRun(runId, 'wf', {});
|
|
224
|
+
|
|
225
|
+
// Store with expired date manually
|
|
226
|
+
// @ts-ignore
|
|
227
|
+
db.db.exec(
|
|
228
|
+
`INSERT INTO idempotency_records (idempotency_key, run_id, step_id, status, created_at, expires_at) VALUES ('exp-key', '${runId}', 's1', 'success', '2000-01-01', '2000-01-02')`
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
const changes = await db.clearExpiredIdempotencyRecord('exp-key');
|
|
232
|
+
expect(changes).toBe(1);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it('should prune idempotency records', async () => {
|
|
236
|
+
const runId = 'run-prune';
|
|
237
|
+
await db.createRun(runId, 'wf', {});
|
|
238
|
+
// @ts-ignore
|
|
239
|
+
db.db.exec(
|
|
240
|
+
`INSERT INTO idempotency_records (idempotency_key, run_id, step_id, status, created_at, expires_at) VALUES ('prune-key', '${runId}', 's1', 'success', '2000-01-01', '2000-01-02')`
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
const changes = await db.pruneIdempotencyRecords();
|
|
244
|
+
expect(changes).toBeGreaterThanOrEqual(1);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it('should list and clear all idempotency records', async () => {
|
|
248
|
+
const runId = 'run-list';
|
|
249
|
+
await db.createRun(runId, 'wf', {});
|
|
250
|
+
await db.storeIdempotencyRecord('k1', runId, 's1', 'success');
|
|
251
|
+
|
|
252
|
+
const list = await db.listIdempotencyRecords(runId);
|
|
253
|
+
expect(list).toHaveLength(1);
|
|
254
|
+
|
|
255
|
+
const allList = await db.listIdempotencyRecords();
|
|
256
|
+
expect(allList.length).toBeGreaterThanOrEqual(1);
|
|
257
|
+
|
|
258
|
+
await db.clearAllIdempotencyRecords();
|
|
259
|
+
const after = await db.listIdempotencyRecords();
|
|
260
|
+
expect(after).toHaveLength(0);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it('should clear idempotency records for specific steps', async () => {
|
|
264
|
+
const runId = 'run-idemp-steps';
|
|
265
|
+
await db.createRun(runId, 'wf', {});
|
|
266
|
+
await db.storeIdempotencyRecord('k1', runId, 's1', 'success');
|
|
267
|
+
await db.storeIdempotencyRecord('k2', runId, 's2', 'success');
|
|
268
|
+
|
|
269
|
+
await db.clearIdempotencyRecordsForSteps(runId, ['s1']);
|
|
270
|
+
const list = await db.listIdempotencyRecords(runId);
|
|
271
|
+
expect(list).toHaveLength(1);
|
|
272
|
+
expect(list[0].step_id).toBe('s2');
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
describe('Retry Logic', () => {
|
|
277
|
+
it('should retry on SQLITE_BUSY', async () => {
|
|
278
|
+
let attempts = 0;
|
|
279
|
+
const result = await (db as any).withRetry(() => {
|
|
280
|
+
attempts++;
|
|
281
|
+
if (attempts < 3) {
|
|
282
|
+
const err = new Error('database is locked');
|
|
283
|
+
(err as any).code = 'SQLITE_BUSY';
|
|
284
|
+
throw err;
|
|
285
|
+
}
|
|
286
|
+
return 'success';
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
expect(result).toBe('success');
|
|
290
|
+
expect(attempts).toBe(3);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it('should throw after max retries', async () => {
|
|
294
|
+
let attempts = 0;
|
|
295
|
+
const operation = () => {
|
|
296
|
+
attempts++;
|
|
297
|
+
const err = new Error('database is locked');
|
|
298
|
+
(err as any).code = 'SQLITE_BUSY';
|
|
299
|
+
throw err;
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
await expect((db as any).withRetry(operation, 2)).rejects.toThrow('database is locked');
|
|
303
|
+
expect(attempts).toBe(2);
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
describe('Timers, Compensations, Cache, Events', () => {
|
|
308
|
+
it('should handle durable timers', async () => {
|
|
309
|
+
const runId = 'run-timer';
|
|
310
|
+
await db.createRun(runId, 'wf', {});
|
|
311
|
+
await db.createTimer('t1', runId, 'step-1', 'sleep', '2025-01-01T00:00:00Z');
|
|
312
|
+
|
|
313
|
+
const timer = await db.getTimer('t1');
|
|
314
|
+
expect(timer?.id).toBe('t1');
|
|
315
|
+
|
|
316
|
+
const pending = await db.getTimerByStep(runId, 'step-1');
|
|
317
|
+
expect(pending?.id).toBe('t1');
|
|
318
|
+
|
|
319
|
+
await db.completeTimer('t1');
|
|
320
|
+
const pendingAfter = await db.getTimerByStep(runId, 'step-1');
|
|
321
|
+
expect(pendingAfter).toBeNull();
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
it('should get pending timers', async () => {
|
|
325
|
+
const runId = 'run-timers';
|
|
326
|
+
await db.createRun(runId, 'wf', {});
|
|
327
|
+
await db.createTimer('t-back', runId, 's1', 'sleep', '2000-01-01T00:00:00Z');
|
|
328
|
+
await db.createTimer('t-future', runId, 's2', 'sleep', '2099-01-01T00:00:00Z');
|
|
329
|
+
|
|
330
|
+
const pending = await db.getPendingTimers();
|
|
331
|
+
expect(pending.some((t) => t.id === 't-back')).toBe(true);
|
|
332
|
+
expect(pending.some((t) => t.id === 't-future')).toBe(false);
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
it('should list and clear timers', async () => {
|
|
336
|
+
const runId = 'run-clear-timers';
|
|
337
|
+
await db.createRun(runId, 'wf', {});
|
|
338
|
+
await db.createTimer('tc1', runId, 's1', 'sleep');
|
|
339
|
+
await db.createTimer('tc2', runId, 's2', 'sleep');
|
|
340
|
+
|
|
341
|
+
const list = await db.listTimers(runId);
|
|
342
|
+
expect(list).toHaveLength(2);
|
|
343
|
+
|
|
344
|
+
await db.clearTimersForSteps(runId, ['s1']);
|
|
345
|
+
expect(await db.listTimers(runId)).toHaveLength(1);
|
|
346
|
+
|
|
347
|
+
await db.clearTimers(runId);
|
|
348
|
+
expect(await db.listTimers(runId)).toHaveLength(0);
|
|
349
|
+
|
|
350
|
+
await db.createTimer('tc3', runId, 's3', 'sleep');
|
|
351
|
+
await db.clearTimers();
|
|
352
|
+
expect(await db.listTimers()).toHaveLength(0);
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
it('should handle compensations', async () => {
|
|
356
|
+
const runId = 'run-comp';
|
|
357
|
+
await db.createRun(runId, 'wf', {});
|
|
358
|
+
await db.registerCompensation('c1', runId, 'step-1', 'comp-step', '{}');
|
|
359
|
+
|
|
360
|
+
const pending = await db.getPendingCompensations(runId);
|
|
361
|
+
expect(pending).toHaveLength(1);
|
|
362
|
+
expect(pending[0].id).toBe('c1');
|
|
363
|
+
|
|
364
|
+
await db.updateCompensationStatus('c1', 'success', '{}');
|
|
365
|
+
const pendingAfter = await db.getPendingCompensations(runId);
|
|
366
|
+
expect(pendingAfter).toHaveLength(0);
|
|
367
|
+
|
|
368
|
+
const all = await db.getAllCompensations(runId);
|
|
369
|
+
expect(all).toHaveLength(1);
|
|
370
|
+
|
|
371
|
+
await db.registerCompensation('c2', runId, 'step-2', 'comp-step-2', '{}');
|
|
372
|
+
await db.clearCompensationsForSteps(runId, ['step-2']);
|
|
373
|
+
const allAfter = await db.getAllCompensations(runId);
|
|
374
|
+
expect(allAfter.some((c) => c.id === 'c2')).toBe(false);
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
it('should handle step cache', async () => {
|
|
378
|
+
await db.storeStepCache('key-c', 'wf', 'step-1', 'output', 3600);
|
|
379
|
+
const cached = await db.getStepCache('key-c');
|
|
380
|
+
expect(JSON.parse(cached?.output || 'null')).toBe('output');
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
it('should handle events', async () => {
|
|
384
|
+
await db.storeEvent('ev-1', 'data');
|
|
385
|
+
const ev = await db.getEvent('ev-1');
|
|
386
|
+
expect(JSON.parse(ev?.data || 'null')).toBe('data');
|
|
387
|
+
|
|
388
|
+
await db.deleteEvent('ev-1');
|
|
389
|
+
const evAfter = await db.getEvent('ev-1');
|
|
390
|
+
expect(evAfter).toBeNull();
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
it('should handle thought events', async () => {
|
|
394
|
+
const runId = 'run-thought';
|
|
395
|
+
await db.createRun(runId, 'wf', {});
|
|
396
|
+
await db.storeThoughtEvent(runId, 'wf', 'step-1', 'thinking...', 'thinking');
|
|
397
|
+
|
|
398
|
+
const thoughts = await db.listThoughtEvents(10, runId);
|
|
399
|
+
expect(thoughts).toHaveLength(1);
|
|
400
|
+
expect(thoughts[0].content).toBe('thinking...');
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
it('should handle batch step creation', async () => {
|
|
404
|
+
const runId = 'run-batch';
|
|
405
|
+
await db.createRun(runId, 'wf', {});
|
|
406
|
+
await db.batchCreateSteps([
|
|
407
|
+
{ id: 'b1', runId, stepId: 's1', iterationIndex: null },
|
|
408
|
+
{ id: 'b2', runId, stepId: 's2', iterationIndex: null },
|
|
409
|
+
]);
|
|
410
|
+
const steps = await db.getStepsByRun(runId);
|
|
411
|
+
expect(steps).toHaveLength(2);
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
it('should clear step executions', async () => {
|
|
415
|
+
const runId = 'run-clear';
|
|
416
|
+
await db.createRun(runId, 'wf', {});
|
|
417
|
+
await db.createStep('e1', runId, 's1');
|
|
418
|
+
await db.createStep('e2', runId, 's2');
|
|
419
|
+
|
|
420
|
+
const changes = await db.clearStepExecutions(runId, ['s1']);
|
|
421
|
+
expect(changes).toBe(1);
|
|
422
|
+
const steps = await db.getStepsByRun(runId);
|
|
423
|
+
expect(steps).toHaveLength(1);
|
|
424
|
+
expect(steps[0].step_id).toBe('s2');
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
it('should get main step', async () => {
|
|
428
|
+
const runId = 'run-main';
|
|
429
|
+
await db.createRun(runId, 'wf', {});
|
|
430
|
+
await db.createStep('e1', runId, 's1'); // null iteration
|
|
431
|
+
|
|
432
|
+
const step = await db.getMainStep(runId, 's1');
|
|
433
|
+
expect(step?.id).toBe('e1');
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
it('should get last run', async () => {
|
|
437
|
+
await db.createRun('last-1', 'last-wf', {});
|
|
438
|
+
await new Promise((r) => setTimeout(r, 10));
|
|
439
|
+
await db.createRun('last-2', 'last-wf', {});
|
|
440
|
+
|
|
441
|
+
const last = await db.getLastRun('last-wf');
|
|
442
|
+
expect(last?.id).toBe('last-2');
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
it('should clear events', async () => {
|
|
446
|
+
await db.storeEvent('e1', 'd1');
|
|
447
|
+
await db.storeEvent('e2', 'd2');
|
|
448
|
+
await db.clearEvents();
|
|
449
|
+
const ev = await db.getEvent('e1');
|
|
450
|
+
expect(ev).toBeNull();
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
it('should clear step cache', async () => {
|
|
454
|
+
await db.storeStepCache('k1', 'wf1', 's1', 'o1');
|
|
455
|
+
await db.storeStepCache('k2', 'wf2', 's2', 'o2');
|
|
456
|
+
|
|
457
|
+
await db.clearStepCache('wf1');
|
|
458
|
+
const c1 = await db.getStepCache('k1');
|
|
459
|
+
expect(c1).toBeNull();
|
|
460
|
+
const c2 = await db.getStepCache('k2');
|
|
461
|
+
expect(c2).not.toBeNull();
|
|
462
|
+
|
|
463
|
+
await db.clearStepCache();
|
|
464
|
+
const c2After = await db.getStepCache('k2');
|
|
465
|
+
expect(c2After).toBeNull();
|
|
466
|
+
});
|
|
467
|
+
});
|
|
154
468
|
});
|