156 lines
5.7 KiB
TypeScript
156 lines
5.7 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
import { mkdtemp, rm } from 'node:fs/promises';
|
|
import { join } from 'node:path';
|
|
import { tmpdir } from 'node:os';
|
|
|
|
import { executeGetCodebaseOverview } from '../tools/codecontext/get_codebase_overview.js';
|
|
import { executeGetFileAnalysis } from '../tools/codecontext/get_file_analysis.js';
|
|
import { executeGetSymbolInfo } from '../tools/codecontext/get_symbol_info.js';
|
|
import { executeSearchSymbols } from '../tools/codecontext/search_symbols.js';
|
|
import { executeGetDependencies } from '../tools/codecontext/get_dependencies.js';
|
|
import { executeWatchChanges } from '../tools/codecontext/watch_changes.js';
|
|
import { executeGetSemanticNeighborhoods } from '../tools/codecontext/get_semantic_neighborhoods.js';
|
|
import { executeGetFrameworkAnalysis } from '../tools/codecontext/get_framework_analysis.js';
|
|
|
|
// ---- fixtures ---------------------------------------------------------------
|
|
|
|
let projectDir: string;
|
|
|
|
beforeEach(async () => {
|
|
projectDir = await mkdtemp(join(tmpdir(), 'codecontext-tools-test-'));
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await rm(projectDir, { recursive: true, force: true });
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
function mockJSONResponse(body: unknown, status = 200): Response {
|
|
return new Response(JSON.stringify(body), {
|
|
status,
|
|
headers: { 'content-type': 'application/json' },
|
|
});
|
|
}
|
|
|
|
// Stub fetcher that records every call and returns a canned successful body.
|
|
// Each test inspects fetcher.mock.calls[0] to assert URL + body shape.
|
|
function makeStub() {
|
|
return vi.fn().mockResolvedValue(
|
|
mockJSONResponse({ result: 'wrapped ok', error: null }),
|
|
);
|
|
}
|
|
|
|
function parsePOST(fetcher: ReturnType<typeof makeStub>): {
|
|
url: string;
|
|
body: Record<string, unknown>;
|
|
} {
|
|
expect(fetcher).toHaveBeenCalledTimes(1);
|
|
const [url, init] = fetcher.mock.calls[0]! as [string, { body: string }];
|
|
return { url, body: JSON.parse(init.body) };
|
|
}
|
|
|
|
// ---- per-wrapper smoke tests -----------------------------------------------
|
|
|
|
describe('codecontext wrappers — toolName + args forwarding', () => {
|
|
it('get_codebase_overview posts to /v1/get_codebase_overview with include_stats default true', async () => {
|
|
const fetcher = makeStub();
|
|
await executeGetCodebaseOverview({}, projectDir, fetcher as unknown as typeof fetch);
|
|
const { url, body } = parsePOST(fetcher);
|
|
expect(url).toMatch(/\/v1\/get_codebase_overview$/);
|
|
expect(body).toMatchObject({ include_stats: true, target_dir: projectDir });
|
|
});
|
|
|
|
it('get_file_analysis forwards file_path', async () => {
|
|
const fetcher = makeStub();
|
|
await executeGetFileAnalysis(
|
|
{ file_path: 'apps/server/src/index.ts' },
|
|
projectDir,
|
|
fetcher as unknown as typeof fetch,
|
|
);
|
|
const { url, body } = parsePOST(fetcher);
|
|
expect(url).toMatch(/\/v1\/get_file_analysis$/);
|
|
expect(body).toMatchObject({
|
|
file_path: 'apps/server/src/index.ts',
|
|
target_dir: projectDir,
|
|
});
|
|
});
|
|
|
|
it('get_symbol_info forwards symbol_name and omits optional fields when unset', async () => {
|
|
const fetcher = makeStub();
|
|
await executeGetSymbolInfo(
|
|
{ symbol_name: 'buildSystemPrompt' },
|
|
projectDir,
|
|
fetcher as unknown as typeof fetch,
|
|
);
|
|
const { url, body } = parsePOST(fetcher);
|
|
expect(url).toMatch(/\/v1\/get_symbol_info$/);
|
|
expect(body).toMatchObject({ symbol_name: 'buildSystemPrompt', target_dir: projectDir });
|
|
expect(body).not.toHaveProperty('file_path');
|
|
expect(body).not.toHaveProperty('framework_type');
|
|
});
|
|
|
|
it('search_symbols defaults limit to 20 and forwards filters when set', async () => {
|
|
const fetcher = makeStub();
|
|
await executeSearchSymbols(
|
|
{ query: 'User', symbol_type: 'class' },
|
|
projectDir,
|
|
fetcher as unknown as typeof fetch,
|
|
);
|
|
const { url, body } = parsePOST(fetcher);
|
|
expect(url).toMatch(/\/v1\/search_symbols$/);
|
|
expect(body).toMatchObject({
|
|
query: 'User',
|
|
symbol_type: 'class',
|
|
limit: 20,
|
|
target_dir: projectDir,
|
|
});
|
|
});
|
|
|
|
it('get_dependencies defaults direction to "both"', async () => {
|
|
const fetcher = makeStub();
|
|
await executeGetDependencies({}, projectDir, fetcher as unknown as typeof fetch);
|
|
const { url, body } = parsePOST(fetcher);
|
|
expect(url).toMatch(/\/v1\/get_dependencies$/);
|
|
expect(body).toMatchObject({ direction: 'both', target_dir: projectDir });
|
|
expect(body).not.toHaveProperty('file_path');
|
|
});
|
|
|
|
it('watch_changes forwards enable=false', async () => {
|
|
const fetcher = makeStub();
|
|
await executeWatchChanges(
|
|
{ enable: false },
|
|
projectDir,
|
|
fetcher as unknown as typeof fetch,
|
|
);
|
|
const { url, body } = parsePOST(fetcher);
|
|
expect(url).toMatch(/\/v1\/watch_changes$/);
|
|
expect(body).toMatchObject({ enable: false, target_dir: projectDir });
|
|
});
|
|
|
|
it('get_semantic_neighborhoods defaults max_results to 10', async () => {
|
|
const fetcher = makeStub();
|
|
await executeGetSemanticNeighborhoods(
|
|
{},
|
|
projectDir,
|
|
fetcher as unknown as typeof fetch,
|
|
);
|
|
const { url, body } = parsePOST(fetcher);
|
|
expect(url).toMatch(/\/v1\/get_semantic_neighborhoods$/);
|
|
expect(body).toMatchObject({ max_results: 10, target_dir: projectDir });
|
|
});
|
|
|
|
it('get_framework_analysis sends only target_dir when no args are provided', async () => {
|
|
const fetcher = makeStub();
|
|
await executeGetFrameworkAnalysis(
|
|
{},
|
|
projectDir,
|
|
fetcher as unknown as typeof fetch,
|
|
);
|
|
const { url, body } = parsePOST(fetcher);
|
|
expect(url).toMatch(/\/v1\/get_framework_analysis$/);
|
|
expect(body).toMatchObject({ target_dir: projectDir });
|
|
expect(body).not.toHaveProperty('framework');
|
|
expect(body).not.toHaveProperty('include_stats');
|
|
});
|
|
});
|