mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-06-28 04:07:32 -07:00
Feat/tool registry discovery (#28113)
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import {
|
||||
buildToolRegistry,
|
||||
resolveToolName,
|
||||
getToolsByCategory,
|
||||
type ToolCategory,
|
||||
} from '../utils/tool-registry.js';
|
||||
|
||||
describe('tool-registry', () => {
|
||||
const registry = buildToolRegistry();
|
||||
|
||||
describe('buildToolRegistry', () => {
|
||||
it('includes all canonical built-in tools', () => {
|
||||
expect(registry.totalTools).toBeGreaterThanOrEqual(26);
|
||||
});
|
||||
|
||||
it('every tool has a valid category', () => {
|
||||
for (const [name, entry] of registry.tools) {
|
||||
expect(entry.category).toBeTruthy();
|
||||
expect(entry.name).toBe(name);
|
||||
}
|
||||
});
|
||||
|
||||
it('byCategory entries match tools map', () => {
|
||||
let categoryTotal = 0;
|
||||
for (const [, entries] of registry.byCategory) {
|
||||
for (const entry of entries) {
|
||||
expect(registry.tools.get(entry.name)).toBe(entry);
|
||||
}
|
||||
categoryTotal += entries.length;
|
||||
}
|
||||
expect(categoryTotal).toBe(registry.totalTools);
|
||||
});
|
||||
|
||||
it('aliasLookup covers every canonical name', () => {
|
||||
for (const name of registry.tools.keys()) {
|
||||
expect(registry.aliasLookup.get(name)).toBe(name);
|
||||
}
|
||||
});
|
||||
|
||||
it('aliasLookup covers every legacy alias', () => {
|
||||
for (const [, entry] of registry.tools) {
|
||||
for (const alias of entry.aliases) {
|
||||
expect(registry.aliasLookup.get(alias)).toBe(entry.name);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('is deterministic across calls', () => {
|
||||
const second = buildToolRegistry();
|
||||
expect([...second.tools.keys()]).toEqual([...registry.tools.keys()]);
|
||||
expect(second.totalTools).toBe(registry.totalTools);
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolveToolName', () => {
|
||||
it('resolves canonical names to themselves', () => {
|
||||
expect(resolveToolName(registry, 'grep_search')).toBe('grep_search');
|
||||
expect(resolveToolName(registry, 'run_shell_command')).toBe(
|
||||
'run_shell_command',
|
||||
);
|
||||
});
|
||||
|
||||
it('resolves legacy alias to canonical name', () => {
|
||||
expect(resolveToolName(registry, 'search_file_content')).toBe(
|
||||
'grep_search',
|
||||
);
|
||||
});
|
||||
|
||||
it('returns undefined for unknown tool names', () => {
|
||||
expect(resolveToolName(registry, 'nonexistent_tool')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('returns undefined for empty string', () => {
|
||||
expect(resolveToolName(registry, '')).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getToolsByCategory', () => {
|
||||
it('returns file-system tools', () => {
|
||||
const tools = getToolsByCategory(registry, 'file-system');
|
||||
const names = tools.map((t) => t.name);
|
||||
expect(names).toContain('glob');
|
||||
expect(names).toContain('grep_search');
|
||||
expect(names).toContain('read_file');
|
||||
expect(names).toContain('write_file');
|
||||
expect(names).toContain('replace');
|
||||
});
|
||||
|
||||
it('returns task-tracker tools', () => {
|
||||
const tools = getToolsByCategory(registry, 'task-tracker');
|
||||
const names = tools.map((t) => t.name);
|
||||
expect(names).toContain('tracker_create_task');
|
||||
expect(names).toContain('tracker_update_task');
|
||||
expect(names).toContain('tracker_get_task');
|
||||
expect(names).toContain('tracker_list_tasks');
|
||||
expect(names).toContain('tracker_add_dependency');
|
||||
expect(names).toContain('tracker_visualize');
|
||||
expect(names).toHaveLength(6);
|
||||
});
|
||||
|
||||
it('returns agent tools', () => {
|
||||
const tools = getToolsByCategory(registry, 'agent');
|
||||
const names = tools.map((t) => t.name);
|
||||
expect(names).toContain('invoke_agent');
|
||||
expect(names).toContain('complete_task');
|
||||
expect(names).toContain('update_topic');
|
||||
});
|
||||
|
||||
it('returns empty array for unknown category', () => {
|
||||
expect(
|
||||
getToolsByCategory(registry, 'nonexistent' as ToolCategory),
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
it('every defined category has at least one tool', () => {
|
||||
const expectedCategories: ToolCategory[] = [
|
||||
'file-system',
|
||||
'shell',
|
||||
'web',
|
||||
'planning',
|
||||
'user-interaction',
|
||||
'skills',
|
||||
'task-tracker',
|
||||
'agent',
|
||||
'mcp',
|
||||
];
|
||||
for (const cat of expectedCategories) {
|
||||
expect(getToolsByCategory(registry, cat).length).toBeGreaterThan(0);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user