test: cover orchestrator admin boundary

This commit is contained in:
john
2026-07-25 07:20:25 +08:00
parent 2596c8f2c6
commit 10e24be243
2 changed files with 177 additions and 0 deletions
+1
View File
@@ -12,6 +12,7 @@
"local_restart": "bash scripts/local_restart.sh", "local_restart": "bash scripts/local_restart.sh",
"pro_restart": "bash scripts/pro_restart.sh", "pro_restart": "bash scripts/pro_restart.sh",
"build": "vite build", "build": "vite build",
"test:orchestrator": "node --test server/orchestrator-routes.test.mjs",
"preview": "node scripts/preview.mjs", "preview": "node scripts/preview.mjs",
"dev:preview": "vite preview" "dev:preview": "vite preview"
}, },
+176
View File
@@ -0,0 +1,176 @@
import assert from 'node:assert/strict';
import { once } from 'node:events';
import test from 'node:test';
import { createAdminApp } from './app.mjs';
function createServices({ role = 'admin' } = {}) {
const calls = {
update: [],
shadow: [],
plans: [],
detail: [],
};
const configState = {
config: {
mode: 'shadow',
primaryEngine: 'langgraph',
fallbackEngine: 'native',
serviceUrl: 'http://127.0.0.1:8093',
requestTimeoutMs: 5000,
rolloutPercent: 0,
userAllowlist: [],
workflowAllowlist: ['code-run-v1'],
fallbackToNative: true,
requireHealthy: true,
executionEnabled: false,
},
configVersion: 4,
updatedBy: 'admin-user',
updatedAt: 1,
source: 'admin-db',
runtime: {
effective: true,
shadowsLangGraph: true,
executesLangGraph: false,
executionHandoff: { enabled: false },
},
engines: [],
executors: [],
};
return {
calls,
services: {
ready: Promise.resolve(),
parseCookies: () => ({ test_session: 'token' }),
USER_COOKIE: 'test_session',
userLoginCookies: () => [],
clearUserSessionCookie: () => {},
resolveCookieDomainForRequest: () => undefined,
userAuth: {
getMe: async () => ({ id: 'john-id', username: 'john', role }),
},
orchestratorConfigService: {
getAdminConfig: async () => configState,
updateAdminConfig: async (config, context) => {
calls.update.push({ config, context });
return { ...configState, config, updatedBy: context.updatedBy };
},
getRuntimeState: async ({ probe }) => ({
...configState,
serviceHealth: { ok: true, status: 'healthy', probe },
}),
},
orchestratorObservabilityService: {
listShadowRuns: async (query) => {
calls.shadow.push(query);
return { metrics: { observations: 0 }, runs: [] };
},
listExecutionPlans: async (query) => {
calls.plans.push(query);
return { metrics: { decisions: 0 }, plans: [] };
},
getCanaryReadiness: async () => ({
ready: false,
recommendation: 'keep_shadow',
}),
getShadowRun: async (runId) => {
calls.detail.push(runId);
return runId === 'missing' ? null : { native: { runId }, remote: { available: true } };
},
},
},
};
}
async function startApp(options) {
const harness = createServices(options);
const server = createAdminApp(harness.services).listen(0, '127.0.0.1');
await once(server, 'listening');
const address = server.address();
assert.ok(address && typeof address === 'object');
return {
...harness,
request: (path, init = {}) => fetch(`http://127.0.0.1:${address.port}${path}`, {
...init,
headers: {
Cookie: 'test_session=token',
'Content-Type': 'application/json',
...init.headers,
},
}),
close: () => new Promise((resolve, reject) => {
server.close((error) => error ? reject(error) : resolve());
}),
};
}
test('admin can read orchestrator config, runtime and observability endpoints', async (t) => {
const app = await startApp();
t.after(app.close);
const config = await app.request('/admin-api/orchestrator/config');
assert.equal(config.status, 200);
assert.equal((await config.json()).config.mode, 'shadow');
const runtime = await app.request('/admin-api/orchestrator/runtime');
assert.equal(runtime.status, 200);
assert.deepEqual((await runtime.json()).serviceHealth, {
ok: true,
status: 'healthy',
probe: true,
});
const shadow = await app.request(
'/admin-api/orchestrator/shadow-runs?hours=24&limit=50&status=failed',
);
assert.equal(shadow.status, 200);
assert.deepEqual(app.calls.shadow, [{ hours: '24', limit: '50', status: 'failed' }]);
const plans = await app.request(
'/admin-api/orchestrator/execution-plans?hours=168&limit=20&selection=native',
);
assert.equal(plans.status, 200);
assert.deepEqual(app.calls.plans, [{ hours: '168', limit: '20', selection: 'native' }]);
const readiness = await app.request('/admin-api/orchestrator/canary-readiness');
assert.equal(readiness.status, 200);
assert.equal((await readiness.json()).recommendation, 'keep_shadow');
const detail = await app.request('/admin-api/orchestrator/shadow-runs/run-1');
assert.equal(detail.status, 200);
assert.deepEqual(app.calls.detail, ['run-1']);
const missing = await app.request('/admin-api/orchestrator/shadow-runs/missing');
assert.equal(missing.status, 404);
});
test('admin config update records the authenticated administrator', async (t) => {
const app = await startApp();
t.after(app.close);
const response = await app.request('/admin-api/orchestrator/config', {
method: 'PUT',
body: JSON.stringify({ config: { mode: 'off', executionEnabled: false } }),
});
assert.equal(response.status, 200);
assert.deepEqual(app.calls.update, [{
config: { mode: 'off', executionEnabled: false },
context: { updatedBy: 'john-id' },
}]);
});
test('ordinary users cannot access or mutate orchestrator controls', async (t) => {
const app = await startApp({ role: 'user' });
t.after(app.close);
const read = await app.request('/admin-api/orchestrator/config');
assert.equal(read.status, 403);
const update = await app.request('/admin-api/orchestrator/config', {
method: 'PUT',
body: JSON.stringify({ config: { mode: 'active', executionEnabled: true } }),
});
assert.equal(update.status, 403);
assert.equal(app.calls.update.length, 0);
});