Files
AtreidesAssetsFree/tests/server/mcp-stdio.test.mjs
T

91 lines
3.7 KiB
JavaScript
Raw Permalink Normal View History

2026-10-29 11:57:29 +01:00
// Generated, do not edit. sha256:7bb4b4b51bf14bf2fa32107d316282a41f1c7586c33fc94c43098199bfa75953
import { describe, it, before, after } from 'node:test';
import assert from 'node:assert/strict';
import { spawn } from 'node:child_process';
import { DatabaseSync } from 'node:sqlite';
import { mkdtemp, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { fileURLToPath } from 'node:url';
const WORTEL = join(fileURLToPath(import.meta.url), '..', '..', '..');
const STDIO = join(WORTEL, 'server', 'mcp', 'stdio.mjs');
let dir, proc, buffer = '', wachtenden = [];
function stuur(bericht) {
return new Promise((resolve, reject) => {
wachtenden.push(resolve);
const klaar = setTimeout(() => reject(new Error('geen antwoord binnen 10s')), 10_000);
wachtenden[wachtenden.length - 1] = (a) => { clearTimeout(klaar); resolve(a); };
proc.stdin.write(`${JSON.stringify(bericht)}\n`);
});
}
before(async () => {
dir = await mkdtemp(join(tmpdir(), 'assetapp-mcp-stdio-'));
proc = spawn(process.execPath, [STDIO], {
env: { ...process.env, ASSETAPP_DATA_DIR: dir, ASSETAPP_DB_FILE: join(dir, 'db.sqlite') },
stdio: ['pipe', 'pipe', 'pipe'],
});
proc.stdout.on('data', (brok) => {
buffer += brok.toString();
let n;
while ((n = buffer.indexOf('\n')) >= 0) {
const regel = buffer.slice(0, n).trim();
buffer = buffer.slice(n + 1);
if (regel) (wachtenden.shift() || (() => {}))(JSON.parse(regel));
}
});
await new Promise((resolve) => proc.stderr.once('data', resolve));
});
after(async () => {
proc.stdin.end();
await new Promise((resolve) => proc.once('exit', resolve));
await rm(dir, { recursive: true, force: true });
});
describe('mcp-stdio: het proces beantwoordt bericht na bericht', () => {
it('antwoordt op initialize', async () => {
const a = await stuur({ jsonrpc: '2.0', id: 1, method: 'initialize', params: {} });
assert.equal(a.id, 1);
assert.ok(a.result.serverInfo.name, 'er hoort een servernaam in te staan');
});
it('geeft de toollijst, en doet dat twee keer achter elkaar', async () => {
const een = await stuur({ jsonrpc: '2.0', id: 2, method: 'tools/list', params: {} });
assert.ok(een.result.tools.length > 0, 'er hoort minstens één tool te zijn');
const twee = await stuur({ jsonrpc: '2.0', id: 3, method: 'tools/list', params: {} });
assert.deepEqual(twee.result.tools.map(t => t.name), een.result.tools.map(t => t.name));
});
it('voert een tool uit en legt hem vast in het auditlog', async () => {
const a = await stuur({
jsonrpc: '2.0', id: 4, method: 'tools/call',
params: { name: 'connection_test', arguments: {} },
});
assert.equal(a.id, 4);
assert.equal(a.error, undefined, `geen fout verwacht: ${JSON.stringify(a.error)}`);
assert.ok(a.result.content[0].text, 'er hoort een antwoord in te staan');
});
});
describe('mcp-stdio: de schuif "Actief" zet dit proces uit', () => {
it('stopt bij het eerstvolgende bericht zodra de schuif uit gaat', async () => {
const db = new DatabaseSync(join(dir, 'db.sqlite'));
db.exec(`PRAGMA busy_timeout = 5000`);
db.exec(`INSERT INTO Preference (scope, key, value) VALUES ('ai', 'enabled', '0')
ON CONFLICT (scope, key) DO UPDATE SET value = '0'`);
db.close();
const antwoord = await stuur({ jsonrpc: '2.0', id: 99, method: 'tools/list', params: {} });
assert.ok(antwoord.error, 'het laatste bericht hoort nog een antwoord te krijgen');
assert.match(antwoord.error.message, /Actief/, 'en dat antwoord wijst de juiste schuif aan');
const code = await new Promise((klaar) => proc.once('exit', klaar));
assert.equal(code, 0, 'een uitgezette koppeling is een keuze en geen storing');
});
});