feat: add pluggable workflow orchestrator controls
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import {
|
||||
createOrchestratorAdminConfigService,
|
||||
defaultOrchestratorConfig,
|
||||
normalizeOrchestratorConfig,
|
||||
} from './admin-config.mjs';
|
||||
|
||||
function createPool() {
|
||||
let row = null;
|
||||
return {
|
||||
async query(sql, params = []) {
|
||||
if (sql.includes('CREATE TABLE')) return [[], []];
|
||||
if (sql.includes('SELECT config_json')) return [[...(row ? [row] : [])], []];
|
||||
if (sql.includes('INSERT INTO')) {
|
||||
row = {
|
||||
config_json: params[1],
|
||||
config_version: params[2],
|
||||
updated_by: params[3],
|
||||
updated_at: params[4],
|
||||
};
|
||||
return [{ affectedRows: 1 }, []];
|
||||
}
|
||||
throw new Error(`Unexpected SQL: ${sql}`);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
test('orchestrator config defaults to a disabled native-safe runtime', async () => {
|
||||
const service = createOrchestratorAdminConfigService(createPool(), { env: {} });
|
||||
const state = await service.getAdminConfig();
|
||||
assert.equal(state.config.mode, 'off');
|
||||
assert.equal(state.config.primaryEngine, 'langgraph');
|
||||
assert.equal(state.runtime.effective, false);
|
||||
assert.equal(state.runtime.reason, 'mode_off');
|
||||
assert.equal(state.engines.find((engine) => engine.id === 'native').configured, true);
|
||||
assert.equal(state.engines.find((engine) => engine.id === 'langgraph').configured, false);
|
||||
});
|
||||
|
||||
test('orchestrator config normalizes unsafe values and keeps a workflow allowlist', () => {
|
||||
const normalized = normalizeOrchestratorConfig({
|
||||
mode: 'active',
|
||||
serviceUrl: 'file:///tmp/graph',
|
||||
requestTimeoutMs: 1,
|
||||
rolloutPercent: 500,
|
||||
workflowAllowlist: ['code-run-v1', 'INVALID WORKFLOW', 'code-run-v1'],
|
||||
});
|
||||
assert.equal(normalized.serviceUrl, '');
|
||||
assert.equal(normalized.requestTimeoutMs, 500);
|
||||
assert.equal(normalized.rolloutPercent, 100);
|
||||
assert.deepEqual(normalized.workflowAllowlist, ['code-run-v1']);
|
||||
});
|
||||
|
||||
test('orchestrator config persists versioned admin updates and selects canary users', async () => {
|
||||
const service = createOrchestratorAdminConfigService(createPool(), { env: {} });
|
||||
const updated = await service.updateAdminConfig({
|
||||
...defaultOrchestratorConfig(),
|
||||
mode: 'canary',
|
||||
serviceUrl: 'http://127.0.0.1:8093',
|
||||
rolloutPercent: 0,
|
||||
userAllowlist: ['user-1'],
|
||||
}, { updatedBy: 'admin-1' });
|
||||
assert.equal(updated.configVersion, 1);
|
||||
assert.equal(updated.updatedBy, 'admin-1');
|
||||
assert.equal(updated.runtime.effective, true);
|
||||
|
||||
const selected = await service.selectEngine({
|
||||
runId: 'run-1',
|
||||
userId: 'user-1',
|
||||
workflowName: 'code-run-v1',
|
||||
});
|
||||
assert.equal(selected.engine, 'langgraph');
|
||||
assert.equal(selected.reason, 'user_allowlist');
|
||||
|
||||
const native = await service.selectEngine({
|
||||
runId: 'run-2',
|
||||
userId: 'user-2',
|
||||
workflowName: 'code-run-v1',
|
||||
});
|
||||
assert.equal(native.engine, 'native');
|
||||
assert.equal(native.reason, 'canary_not_selected');
|
||||
});
|
||||
|
||||
test('orchestrator emergency kill switch always forces native selection', async () => {
|
||||
const service = createOrchestratorAdminConfigService(createPool(), {
|
||||
env: { MEMIND_ORCHESTRATOR_KILL_SWITCH: '1' },
|
||||
});
|
||||
await service.updateAdminConfig({
|
||||
mode: 'active',
|
||||
serviceUrl: 'http://127.0.0.1:8093',
|
||||
});
|
||||
const selected = await service.selectEngine({
|
||||
runId: 'run-1',
|
||||
userId: 'user-1',
|
||||
workflowName: 'code-run-v1',
|
||||
});
|
||||
assert.equal(selected.engine, 'native');
|
||||
assert.equal(selected.reason, 'kill_switch');
|
||||
});
|
||||
Reference in New Issue
Block a user