Files
memind/services/orchestrator/execution-adapter.test.mjs

150 lines
4.6 KiB
JavaScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { createWorkflowEngineRegistry } from './engine-registry.mjs';
import {
createWorkflowExecutionAdapter,
normalizeWorkflowExecutionRequest,
} from './execution-adapter.mjs';
function engine(id) {
return {
id,
start() {
throw new Error('must not dispatch during Phase 3 dry-run');
},
resume() {},
cancel() {},
getState() {},
async *streamEvents() {},
};
}
function executionRequest(overrides = {}) {
return {
spec: {
runId: 'run-1',
requestId: 'request-1',
workflow: { name: 'code-run-v1', version: 1 },
subject: { userId: 'user-1' },
},
idempotencyKey: 'agent-run:run-1:attempt-1',
authorization: { executionAllowed: true, actorId: 'user-1' },
controls: { timeoutMs: 10_000 },
...overrides,
};
}
test('workflow execution request normalizes authorization, limits and idempotency', () => {
const request = normalizeWorkflowExecutionRequest(executionRequest({
idempotencyKey: ` ${'x'.repeat(250)} `,
controls: { timeoutMs: 120_000, fallbackAllowed: false },
}));
assert.equal(request.version, 'workflow-execution-request-v1');
assert.equal(request.idempotencyKey.length, 200);
assert.equal(request.authorization.executionAllowed, true);
assert.equal(request.controls.timeoutMs, 60_000);
assert.equal(request.controls.fallbackAllowed, false);
});
test('workflow execution adapter records a LangGraph candidate but keeps Native effective', async () => {
const registry = createWorkflowEngineRegistry([engine('native'), engine('langgraph')]);
const adapter = createWorkflowExecutionAdapter({
engineRegistry: registry,
configService: {
async selectEngine() {
return {
engine: 'native',
candidateEngine: 'langgraph',
fallbackEngine: 'native',
mode: 'canary',
reason: 'execution_gate_disabled',
candidateReason: 'user_allowlist',
configVersion: 4,
};
},
},
});
const decision = await adapter.plan(executionRequest());
assert.equal(decision.version, 'workflow-execution-decision-v1');
assert.equal(decision.candidateEngine, 'langgraph');
assert.equal(decision.effectiveEngine, 'native');
assert.equal(decision.dryRun, true);
assert.equal(decision.handoffAllowed, false);
assert.equal(decision.candidateReason, 'user_allowlist');
assert.deepEqual(decision.gates, {
implementation: true,
routingSelected: false,
engineRegistered: true,
idempotencyKeyPresent: true,
executionAuthorized: true,
killSwitchOpen: true,
});
await assert.rejects(
() => adapter.start(executionRequest()),
(error) => error.code === 'WORKFLOW_EXECUTION_HANDOFF_DISABLED'
&& error.decision.effectiveEngine === 'native',
);
});
test('workflow execution adapter dispatches a selected and authorized LangGraph engine', async () => {
let started = null;
const registry = createWorkflowEngineRegistry([
engine('native'),
{
...engine('langgraph'),
start(spec) {
started = spec;
return { runId: spec.runId, status: 'waiting' };
},
},
]);
const adapter = createWorkflowExecutionAdapter({
engineRegistry: registry,
configService: {
async selectEngine() {
return {
engine: 'langgraph',
candidateEngine: 'langgraph',
fallbackEngine: 'native',
mode: 'canary',
reason: 'user_allowlist',
candidateReason: 'user_allowlist',
configVersion: 5,
};
},
},
});
const result = await adapter.start(executionRequest());
assert.equal(result.dispatched, true);
assert.equal(result.engine, 'langgraph');
assert.equal(result.decision.handoffAllowed, true);
assert.equal(result.decision.dryRun, false);
assert.equal(started.runId, 'run-1');
});
test('workflow execution adapter leaves native dispatch under Portal ownership', async () => {
const registry = createWorkflowEngineRegistry([engine('native')]);
const adapter = createWorkflowExecutionAdapter({
engineRegistry: registry,
configService: {
async selectEngine() {
return {
engine: 'native',
candidateEngine: 'native',
fallbackEngine: 'native',
mode: 'off',
reason: 'mode_off',
configVersion: 1,
};
},
},
});
const result = await adapter.start(executionRequest());
assert.equal(result.dispatched, false);
assert.equal(result.reason, 'native_execution_owned_by_portal');
assert.equal(result.decision.effectiveEngine, 'native');
});