Files
factory/lib/wiki-graph/tests/tools.test.ts
vitya ca669d96e1 feat(lib): bundle MCP servers from .common/lib into factory/lib/
Copies source (no node_modules, dist, .tasks, .wiki, __pycache__) for:
- projects-meta-mcp v2.25.0 (TypeScript/Node)
- wiki-graph v0.3.1 (TypeScript/Node)
- interns-mcp v0.3.3 (Python/FastMCP)

.gitignore: exclude lib build artefacts (node_modules, dist, .venv, __pycache__, *.pyc)
bootstrap.ps1: add MCP build step — npm install+build for TS servers, venv+pip for Python

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-11 13:17:29 +03:00

82 lines
2.5 KiB
TypeScript

import { describe, test, expect } from 'vitest';
import { fileURLToPath } from 'node:url';
import { dirname, join } from 'node:path';
import { makeWikiGraphTools } from '../src/tools.js';
import { GraphCache } from '../src/cache.js';
const here = dirname(fileURLToPath(import.meta.url));
const FIXTURE = join(here, 'fixtures', 'wiki');
function toolMap() {
const tools = makeWikiGraphTools(new GraphCache());
return new Map(tools.map((t) => [t.name, t]));
}
function textOf(res: { content: Array<{ type: string; text: string }>; isError?: boolean }) {
return res.content.map((c) => c.text).join('\n');
}
describe('wiki-graph MCP tools', () => {
const tools = toolMap();
test('exposes exactly the five v1 tools', () => {
expect([...tools.keys()].sort()).toEqual([
'backlinks',
'neighbors',
'orphans',
'path',
'stats',
]);
});
test('every tool requires a corpus argument', () => {
for (const t of tools.values()) {
expect(t.inputSchema.required).toContain('corpus');
}
});
test('path: returns the chain', () => {
const res = tools.get('path')!.handler({ corpus: FIXTURE, from: 'a', to: 'c' });
expect(textOf(res)).toContain('a → b → c');
});
test('path: reports absence', () => {
const res = tools.get('path')!.handler({ corpus: FIXTURE, from: 'a', to: 'e' });
expect(textOf(res).toLowerCase()).toContain('no path');
});
test('neighbors: honors depth', () => {
const res = tools.get('neighbors')!.handler({ corpus: FIXTURE, node: 'a', depth: 2 });
const txt = textOf(res);
expect(txt).toContain('b');
expect(txt).toContain('c');
});
test('backlinks: lists incoming', () => {
const res = tools.get('backlinks')!.handler({ corpus: FIXTURE, node: 'c' });
const txt = textOf(res);
expect(txt).toContain('b');
expect(txt).toContain('d');
});
test('orphans: lists unlinked pages and dangling targets', () => {
const res = tools.get('orphans')!.handler({ corpus: FIXTURE });
const txt = textOf(res);
expect(txt).toContain('e');
expect(txt).toContain('<todo-module>');
});
test('stats: reports counts', () => {
const res = tools.get('stats')!.handler({ corpus: FIXTURE });
const txt = textOf(res);
expect(txt).toContain('5');
expect(txt).toContain('nodes');
});
test('missing corpus is a clean error, not a throw', () => {
const res = tools.get('stats')!.handler({});
expect(res.isError).toBe(true);
expect(textOf(res).toLowerCase()).toContain('corpus');
});
});