onbuzz 3.6.1 → 3.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/__test-utils__/fixtures/malformedJson.js +31 -0
- package/src/__test-utils__/globalSetup.js +9 -0
- package/src/__test-utils__/globalTeardown.js +12 -0
- package/src/__test-utils__/mockFactories.js +101 -0
- package/src/analyzers/__tests__/CSSAnalyzer.test.js +41 -0
- package/src/analyzers/__tests__/ConfigValidator.test.js +362 -0
- package/src/analyzers/__tests__/ESLintAnalyzer.test.js +271 -0
- package/src/analyzers/__tests__/JavaScriptAnalyzer.test.js +40 -0
- package/src/analyzers/__tests__/PrettierFormatter.test.js +197 -0
- package/src/analyzers/__tests__/PythonAnalyzer.test.js +208 -0
- package/src/analyzers/__tests__/SecurityAnalyzer.test.js +303 -0
- package/src/analyzers/__tests__/SparrowAnalyzer.test.js +270 -0
- package/src/analyzers/__tests__/TypeScriptAnalyzer.test.js +187 -0
- package/src/core/__tests__/agentPool.test.js +601 -0
- package/src/core/__tests__/agentScheduler.test.js +576 -0
- package/src/core/__tests__/contextManager.test.js +252 -0
- package/src/core/__tests__/flowExecutor.test.js +262 -0
- package/src/core/__tests__/messageProcessor.test.js +627 -0
- package/src/core/__tests__/orchestrator.test.js +257 -0
- package/src/core/__tests__/stateManager.test.js +375 -0
- package/src/core/agentPool.js +11 -1
- package/src/index.js +25 -9
- package/src/interfaces/terminal/__tests__/smoke/imports.test.js +3 -5
- package/src/services/__tests__/agentActivityService.test.js +319 -0
- package/src/services/__tests__/apiKeyManager.test.js +206 -0
- package/src/services/__tests__/benchmarkService.test.js +184 -0
- package/src/services/__tests__/budgetService.test.js +211 -0
- package/src/services/__tests__/contextInjectionService.test.js +205 -0
- package/src/services/__tests__/conversationCompactionService.test.js +280 -0
- package/src/services/__tests__/credentialVault.test.js +469 -0
- package/src/services/__tests__/errorHandler.test.js +314 -0
- package/src/services/__tests__/fileAttachmentService.test.js +278 -0
- package/src/services/__tests__/flowContextService.test.js +199 -0
- package/src/services/__tests__/memoryService.test.js +450 -0
- package/src/services/__tests__/modelRouterService.test.js +388 -0
- package/src/services/__tests__/modelsService.test.js +261 -0
- package/src/services/__tests__/portRegistry.test.js +123 -0
- package/src/services/__tests__/projectDetector.test.js +34 -0
- package/src/services/__tests__/promptService.test.js +242 -0
- package/src/services/__tests__/qualityInspector.test.js +97 -0
- package/src/services/__tests__/scheduleService.test.js +308 -0
- package/src/services/__tests__/serviceRegistry.test.js +74 -0
- package/src/services/__tests__/skillsService.test.js +402 -0
- package/src/services/__tests__/tokenCountingService.test.js +48 -0
- package/src/tools/__tests__/agentCommunicationTool.test.js +500 -0
- package/src/tools/__tests__/agentDelayTool.test.js +342 -0
- package/src/tools/__tests__/asyncToolManager.test.js +344 -0
- package/src/tools/__tests__/baseTool.test.js +420 -0
- package/src/tools/__tests__/codeMapTool.test.js +348 -0
- package/src/tools/__tests__/fileContentReplaceTool.test.js +309 -0
- package/src/tools/__tests__/fileSystemTool.test.js +717 -0
- package/src/tools/__tests__/fileTreeTool.test.js +274 -0
- package/src/tools/__tests__/helpTool.test.js +204 -0
- package/src/tools/__tests__/jobDoneTool.test.js +296 -0
- package/src/tools/__tests__/memoryTool.test.js +297 -0
- package/src/tools/__tests__/seekTool.test.js +282 -0
- package/src/tools/__tests__/skillsTool.test.js +226 -0
- package/src/tools/__tests__/staticAnalysisTool.test.js +509 -0
- package/src/tools/__tests__/taskManagerTool.test.js +725 -0
- package/src/tools/__tests__/terminalTool.test.js +384 -0
- package/src/tools/__tests__/userPromptTool.test.js +297 -0
- package/src/tools/__tests__/webTool.e2e.test.js +25 -11
- package/src/tools/webTool.js +6 -12
- package/src/types/__tests__/agent.test.js +499 -0
- package/src/types/__tests__/contextReference.test.js +606 -0
- package/src/types/__tests__/conversation.test.js +555 -0
- package/src/types/__tests__/toolCommand.test.js +584 -0
- package/src/types/contextReference.js +1 -1
- package/src/utilities/__tests__/attachmentValidator.test.js +80 -0
- package/src/utilities/__tests__/configManager.test.js +397 -0
- package/src/utilities/__tests__/constants.test.js +49 -0
- package/src/utilities/__tests__/directoryAccessManager.test.js +388 -0
- package/src/utilities/__tests__/fileProcessor.test.js +104 -0
- package/src/utilities/__tests__/jsonRepair.test.js +104 -0
- package/src/utilities/__tests__/logger.test.js +129 -0
- package/src/utilities/__tests__/platformUtils.test.js +87 -0
- package/src/utilities/__tests__/structuredFileValidator.test.js +263 -0
- package/src/utilities/__tests__/tagParser.test.js +887 -0
- package/src/utilities/__tests__/toolConstants.test.js +94 -0
- package/src/utilities/tagParser.js +2 -2
- package/src/tools/browserTool.js +0 -897
- package/src/utilities/platformUtils.test.js +0 -98
- /package/src/tools/{filesystemTool.js → fileSystemTool.js} +0 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import { jest, describe, test, expect, beforeEach } from '@jest/globals';
|
|
2
|
+
import { createMockLogger, createMockConfig } from '../../__test-utils__/mockFactories.js';
|
|
3
|
+
|
|
4
|
+
// Mock fs/promises before import
|
|
5
|
+
const mockFs = {
|
|
6
|
+
stat: jest.fn(),
|
|
7
|
+
readdir: jest.fn()
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
jest.unstable_mockModule('fs', () => ({
|
|
11
|
+
promises: mockFs,
|
|
12
|
+
default: { promises: mockFs }
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
// Mock constants
|
|
16
|
+
jest.unstable_mockModule('../../utilities/constants.js', () => ({
|
|
17
|
+
TOOL_STATUS: { PENDING: 'pending', EXECUTING: 'executing', COMPLETED: 'completed', FAILED: 'failed' },
|
|
18
|
+
OPERATION_STATUS: { NOT_FOUND: 'not_found' },
|
|
19
|
+
ERROR_TYPES: {},
|
|
20
|
+
SYSTEM_DEFAULTS: { MAX_TOOL_EXECUTION_TIME: 300000 }
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
const { default: FileTreeTool } = await import('../fileTreeTool.js');
|
|
24
|
+
|
|
25
|
+
// Helper to create mock dirent
|
|
26
|
+
function mockDirent(name, isDir = false) {
|
|
27
|
+
return {
|
|
28
|
+
name,
|
|
29
|
+
isDirectory: () => isDir,
|
|
30
|
+
isFile: () => !isDir,
|
|
31
|
+
isSymbolicLink: () => false
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
describe('FileTreeTool', () => {
|
|
36
|
+
let tool;
|
|
37
|
+
let logger;
|
|
38
|
+
const context = { projectDir: '/project', agentId: 'agent-1' };
|
|
39
|
+
|
|
40
|
+
beforeEach(() => {
|
|
41
|
+
jest.clearAllMocks();
|
|
42
|
+
logger = createMockLogger();
|
|
43
|
+
tool = new FileTreeTool({}, logger);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('constructor sets metadata correctly', () => {
|
|
47
|
+
expect(tool.id).toBe('file-tree');
|
|
48
|
+
expect(tool.requiresProject).toBe(true);
|
|
49
|
+
expect(tool.isAsync).toBe(true);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('getDescription mentions file tree', () => {
|
|
53
|
+
const desc = tool.getDescription();
|
|
54
|
+
expect(desc).toContain('File Tree Tool');
|
|
55
|
+
expect(desc).toContain('maxDepth');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('getRequiredParameters returns empty array', () => {
|
|
59
|
+
expect(tool.getRequiredParameters()).toEqual([]);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('parseParameters parses JSON content', () => {
|
|
63
|
+
const content = JSON.stringify({
|
|
64
|
+
directory: 'src',
|
|
65
|
+
maxDepth: 5,
|
|
66
|
+
includeExtensions: ['.js'],
|
|
67
|
+
showFiles: true
|
|
68
|
+
});
|
|
69
|
+
const result = tool.parseParameters(content);
|
|
70
|
+
expect(result.directory).toBe('src');
|
|
71
|
+
expect(result.maxDepth).toBe(5);
|
|
72
|
+
expect(result.includeExtensions).toEqual(['.js']);
|
|
73
|
+
expect(result.showFiles).toBe(true);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('parseParameters defaults for missing JSON fields', () => {
|
|
77
|
+
const result = tool.parseParameters('{}');
|
|
78
|
+
expect(result.directory).toBe('.');
|
|
79
|
+
expect(result.maxDepth).toBe(3);
|
|
80
|
+
expect(result.showFiles).toBe(true);
|
|
81
|
+
expect(result.showHidden).toBe(false);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('parseParameters handles XML content', () => {
|
|
85
|
+
const content = '<directory>src</directory><max-depth>4</max-depth><show-files>true</show-files>';
|
|
86
|
+
const result = tool.parseParameters(content);
|
|
87
|
+
expect(result.directory).toBe('src');
|
|
88
|
+
expect(result.maxDepth).toBe(4);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test('parseParameters returns defaults on parse error', () => {
|
|
92
|
+
const result = tool.parseParameters('{ broken');
|
|
93
|
+
expect(result.directory).toBe('.');
|
|
94
|
+
expect(result).toHaveProperty('parseError');
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test('customValidateParameters rejects maxDepth exceeding limit', () => {
|
|
98
|
+
expect(() => tool.customValidateParameters({ maxDepth: 100 })).toThrow();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test('customValidateParameters rejects maxDepth < 1', () => {
|
|
102
|
+
expect(() => tool.customValidateParameters({ maxDepth: 0 })).toThrow();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test('customValidateParameters rejects path traversal', () => {
|
|
106
|
+
expect(() => tool.customValidateParameters({ directory: '../etc' })).toThrow('traversal');
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test('customValidateParameters rejects non-array includeExtensions', () => {
|
|
110
|
+
expect(() => tool.customValidateParameters({ includeExtensions: '.js' })).toThrow();
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test('customValidateParameters rejects extension without dot', () => {
|
|
114
|
+
expect(() => tool.customValidateParameters({ includeExtensions: ['js'] })).toThrow('dot');
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test('customValidateParameters accepts valid params', () => {
|
|
118
|
+
const result = tool.customValidateParameters({
|
|
119
|
+
maxDepth: 3,
|
|
120
|
+
directory: 'src',
|
|
121
|
+
includeExtensions: ['.js'],
|
|
122
|
+
excludeExtensions: ['.test.js'],
|
|
123
|
+
excludeDirectories: ['tests']
|
|
124
|
+
});
|
|
125
|
+
expect(result.valid).toBe(true);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test('execute generates directory tree structure', async () => {
|
|
129
|
+
// Root directory stat
|
|
130
|
+
mockFs.stat.mockResolvedValue({ isDirectory: () => true });
|
|
131
|
+
|
|
132
|
+
// Root readdir
|
|
133
|
+
mockFs.readdir.mockResolvedValueOnce([
|
|
134
|
+
mockDirent('src', true),
|
|
135
|
+
mockDirent('package.json')
|
|
136
|
+
]);
|
|
137
|
+
|
|
138
|
+
// src readdir
|
|
139
|
+
mockFs.readdir.mockResolvedValueOnce([
|
|
140
|
+
mockDirent('index.js'),
|
|
141
|
+
mockDirent('app.js')
|
|
142
|
+
]);
|
|
143
|
+
|
|
144
|
+
const result = await tool.execute(
|
|
145
|
+
{ directory: '.', maxDepth: 3, showFiles: true, showHidden: false },
|
|
146
|
+
context
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
expect(result.success).toBe(true);
|
|
150
|
+
expect(result.tree).toContain('src');
|
|
151
|
+
expect(result.tree).toContain('package.json');
|
|
152
|
+
expect(result.totalFiles).toBeGreaterThanOrEqual(1);
|
|
153
|
+
expect(result.totalDirectories).toBeGreaterThanOrEqual(1);
|
|
154
|
+
expect(result).toHaveProperty('summary');
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test('execute respects depth limit', async () => {
|
|
158
|
+
mockFs.stat.mockResolvedValue({ isDirectory: () => true });
|
|
159
|
+
|
|
160
|
+
// Root readdir with deep nesting
|
|
161
|
+
mockFs.readdir.mockResolvedValueOnce([
|
|
162
|
+
mockDirent('level1', true)
|
|
163
|
+
]);
|
|
164
|
+
|
|
165
|
+
// level1 readdir - at depth 1 with maxDepth 1, files at depth 2 should be excluded
|
|
166
|
+
mockFs.readdir.mockResolvedValueOnce([
|
|
167
|
+
mockDirent('deep.js')
|
|
168
|
+
]);
|
|
169
|
+
|
|
170
|
+
const result = await tool.execute(
|
|
171
|
+
{ directory: '.', maxDepth: 1, showFiles: true, showHidden: false },
|
|
172
|
+
context
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
expect(result.success).toBe(true);
|
|
176
|
+
// Files at depth 2 should not appear when maxDepth is 1
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test('execute ignores node_modules and .git by default', async () => {
|
|
180
|
+
mockFs.stat.mockResolvedValue({ isDirectory: () => true });
|
|
181
|
+
|
|
182
|
+
mockFs.readdir.mockResolvedValueOnce([
|
|
183
|
+
mockDirent('src', true),
|
|
184
|
+
mockDirent('node_modules', true),
|
|
185
|
+
mockDirent('.git', true)
|
|
186
|
+
]);
|
|
187
|
+
|
|
188
|
+
mockFs.readdir.mockResolvedValueOnce([]); // src is empty
|
|
189
|
+
|
|
190
|
+
const result = await tool.execute(
|
|
191
|
+
{ directory: '.', maxDepth: 3, showFiles: true, showHidden: false },
|
|
192
|
+
context
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
expect(result.success).toBe(true);
|
|
196
|
+
expect(result.skippedCount).toBeGreaterThanOrEqual(2); // node_modules, .git skipped
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test('execute handles empty directory', async () => {
|
|
200
|
+
mockFs.stat.mockResolvedValue({ isDirectory: () => true });
|
|
201
|
+
mockFs.readdir.mockResolvedValueOnce([]);
|
|
202
|
+
|
|
203
|
+
const result = await tool.execute(
|
|
204
|
+
{ directory: '.', maxDepth: 3, showFiles: true, showHidden: false },
|
|
205
|
+
context
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
expect(result.success).toBe(true);
|
|
209
|
+
expect(result.totalFiles).toBe(0);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
test('execute throws for non-existent directory', async () => {
|
|
213
|
+
mockFs.stat.mockRejectedValue(new Error('ENOENT'));
|
|
214
|
+
|
|
215
|
+
await expect(tool.execute(
|
|
216
|
+
{ directory: 'nonexistent', maxDepth: 3, showFiles: true, showHidden: false },
|
|
217
|
+
context
|
|
218
|
+
)).rejects.toThrow('does not exist');
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
test('execute with includeExtensions filters files', async () => {
|
|
222
|
+
mockFs.stat.mockResolvedValue({ isDirectory: () => true });
|
|
223
|
+
|
|
224
|
+
mockFs.readdir.mockResolvedValueOnce([
|
|
225
|
+
mockDirent('app.js'),
|
|
226
|
+
mockDirent('style.css'),
|
|
227
|
+
mockDirent('readme.md')
|
|
228
|
+
]);
|
|
229
|
+
|
|
230
|
+
const result = await tool.execute(
|
|
231
|
+
{ directory: '.', maxDepth: 3, showFiles: true, showHidden: false, includeExtensions: ['.js'] },
|
|
232
|
+
context
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
expect(result.success).toBe(true);
|
|
236
|
+
expect(result.tree).toContain('app.js');
|
|
237
|
+
expect(result.tree).not.toContain('style.css');
|
|
238
|
+
expect(result.tree).not.toContain('readme.md');
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
test('formatFileSize formats bytes correctly', () => {
|
|
242
|
+
expect(tool.formatFileSize(500)).toBe('500 B');
|
|
243
|
+
expect(tool.formatFileSize(2048)).toContain('KB');
|
|
244
|
+
expect(tool.formatFileSize(2 * 1024 * 1024)).toContain('MB');
|
|
245
|
+
expect(tool.formatFileSize(2 * 1024 * 1024 * 1024)).toContain('GB');
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
test('generateSummary produces summary text', () => {
|
|
249
|
+
const summary = tool.generateSummary('src', { filesCount: 10, directoriesCount: 3, skippedCount: 2 }, 4);
|
|
250
|
+
expect(summary).toContain('src');
|
|
251
|
+
expect(summary).toContain('10');
|
|
252
|
+
expect(summary).toContain('3');
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
test('formatTree handles null node', () => {
|
|
256
|
+
expect(tool.formatTree(null)).toBe('');
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
test('execute uses directoryAccess working directory', async () => {
|
|
260
|
+
mockFs.stat.mockResolvedValue({ isDirectory: () => true });
|
|
261
|
+
mockFs.readdir.mockResolvedValueOnce([]);
|
|
262
|
+
|
|
263
|
+
const result = await tool.execute(
|
|
264
|
+
{ directory: '.', maxDepth: 2, showFiles: true, showHidden: false },
|
|
265
|
+
{
|
|
266
|
+
projectDir: '/project',
|
|
267
|
+
agentId: 'agent-1',
|
|
268
|
+
directoryAccess: { workingDirectory: '/custom/dir' }
|
|
269
|
+
}
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
expect(result.success).toBe(true);
|
|
273
|
+
});
|
|
274
|
+
});
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { jest, describe, test, expect, beforeEach } from '@jest/globals';
|
|
2
|
+
import { createMockLogger, createMockConfig } from '../../__test-utils__/mockFactories.js';
|
|
3
|
+
|
|
4
|
+
// Mock TagParser before importing HelpTool
|
|
5
|
+
jest.unstable_mockModule('../../utilities/tagParser.js', () => ({
|
|
6
|
+
default: {
|
|
7
|
+
extractContent: jest.fn((content, tag) => {
|
|
8
|
+
const regex = new RegExp(`<${tag}>(.*?)</${tag}>`, 'gs');
|
|
9
|
+
const matches = [];
|
|
10
|
+
let match;
|
|
11
|
+
while ((match = regex.exec(content)) !== null) {
|
|
12
|
+
matches.push(match[1]);
|
|
13
|
+
}
|
|
14
|
+
return matches;
|
|
15
|
+
})
|
|
16
|
+
}
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
const { default: HelpTool } = await import('../helpTool.js');
|
|
20
|
+
|
|
21
|
+
describe('HelpTool', () => {
|
|
22
|
+
let tool;
|
|
23
|
+
let logger;
|
|
24
|
+
let config;
|
|
25
|
+
|
|
26
|
+
beforeEach(() => {
|
|
27
|
+
logger = createMockLogger();
|
|
28
|
+
config = createMockConfig();
|
|
29
|
+
tool = new HelpTool(config, logger);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe('constructor', () => {
|
|
33
|
+
test('should set correct id and metadata', () => {
|
|
34
|
+
expect(tool.id).toBe('help');
|
|
35
|
+
expect(tool.name).toBe('Help Tool');
|
|
36
|
+
expect(tool.version).toBe('1.0.0');
|
|
37
|
+
expect(tool.requiresProject).toBe(false);
|
|
38
|
+
expect(tool.isAsync).toBe(false);
|
|
39
|
+
expect(tool.toolsRegistry).toBeNull();
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe('getDescription', () => {
|
|
44
|
+
test('should return non-empty description string', () => {
|
|
45
|
+
const desc = tool.getDescription();
|
|
46
|
+
expect(typeof desc).toBe('string');
|
|
47
|
+
expect(desc.length).toBeGreaterThan(0);
|
|
48
|
+
expect(desc).toContain('Help Tool');
|
|
49
|
+
expect(desc).toContain('toolId');
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe('setToolsRegistry', () => {
|
|
54
|
+
test('should set the registry reference', () => {
|
|
55
|
+
const mockRegistry = { getTool: jest.fn() };
|
|
56
|
+
tool.setToolsRegistry(mockRegistry);
|
|
57
|
+
expect(tool.toolsRegistry).toBe(mockRegistry);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('getSupportedActions', () => {
|
|
62
|
+
test('should return expected actions', () => {
|
|
63
|
+
const actions = tool.getSupportedActions();
|
|
64
|
+
expect(actions).toContain('get-description');
|
|
65
|
+
expect(actions).toContain('list-tools');
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
describe('parseParameters', () => {
|
|
70
|
+
test('should parse tool name from tags', () => {
|
|
71
|
+
const result = tool.parseParameters('<tool>filesystem</tool>');
|
|
72
|
+
expect(result.tool).toBe('filesystem');
|
|
73
|
+
expect(result.list).toBe(false);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('should parse list=true from tags', () => {
|
|
77
|
+
const result = tool.parseParameters('<list>true</list>');
|
|
78
|
+
expect(result.list).toBe(true);
|
|
79
|
+
expect(result.tool).toBeNull();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test('should return defaults when no tags found', () => {
|
|
83
|
+
const result = tool.parseParameters('some random content');
|
|
84
|
+
expect(result.tool).toBeNull();
|
|
85
|
+
expect(result.list).toBe(false);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe('execute', () => {
|
|
90
|
+
test('should return error when toolsRegistry is not set', async () => {
|
|
91
|
+
const result = await tool.execute({});
|
|
92
|
+
expect(result.success).toBe(false);
|
|
93
|
+
expect(result.error).toContain('not properly initialized');
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test('should return error when no tool specified and list is false', async () => {
|
|
97
|
+
tool.setToolsRegistry({ getTool: jest.fn(), listTools: jest.fn() });
|
|
98
|
+
const result = await tool.execute({ tool: null, list: false });
|
|
99
|
+
expect(result.success).toBe(false);
|
|
100
|
+
expect(result.error).toContain('No tool specified');
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test('should list tools when list=true', async () => {
|
|
104
|
+
const mockRegistry = {
|
|
105
|
+
getTool: jest.fn().mockReturnValue({ isEnabled: true }),
|
|
106
|
+
listTools: jest.fn().mockReturnValue(['filesystem', 'terminal']),
|
|
107
|
+
toolSummaries: new Map([
|
|
108
|
+
['filesystem', 'File operations'],
|
|
109
|
+
['terminal', 'Run commands']
|
|
110
|
+
])
|
|
111
|
+
};
|
|
112
|
+
tool.setToolsRegistry(mockRegistry);
|
|
113
|
+
|
|
114
|
+
const result = await tool.execute({ list: true });
|
|
115
|
+
expect(result.success).toBe(true);
|
|
116
|
+
expect(result.action).toBe('list-tools');
|
|
117
|
+
expect(result.tools).toHaveLength(2);
|
|
118
|
+
expect(result.output).toContain('filesystem');
|
|
119
|
+
expect(result.output).toContain('terminal');
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test('should list tools with disabled indicator', async () => {
|
|
123
|
+
const mockRegistry = {
|
|
124
|
+
getTool: jest.fn().mockReturnValue({ isEnabled: false }),
|
|
125
|
+
listTools: jest.fn().mockReturnValue(['web']),
|
|
126
|
+
toolSummaries: new Map([['web', 'Web browsing']])
|
|
127
|
+
};
|
|
128
|
+
tool.setToolsRegistry(mockRegistry);
|
|
129
|
+
|
|
130
|
+
const result = await tool.execute({ list: true });
|
|
131
|
+
expect(result.output).toContain('(disabled)');
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test('should get tool description for valid tool', async () => {
|
|
135
|
+
const mockTool = {
|
|
136
|
+
getDescription: jest.fn().mockReturnValue('Full description of filesystem'),
|
|
137
|
+
getCapabilities: jest.fn().mockReturnValue({
|
|
138
|
+
supportedActions: ['read', 'write'],
|
|
139
|
+
async: false,
|
|
140
|
+
requiresProject: true
|
|
141
|
+
})
|
|
142
|
+
};
|
|
143
|
+
const mockRegistry = {
|
|
144
|
+
getTool: jest.fn().mockReturnValue(mockTool),
|
|
145
|
+
listTools: jest.fn().mockReturnValue(['filesystem'])
|
|
146
|
+
};
|
|
147
|
+
tool.setToolsRegistry(mockRegistry);
|
|
148
|
+
|
|
149
|
+
const result = await tool.execute({ tool: 'filesystem' });
|
|
150
|
+
expect(result.success).toBe(true);
|
|
151
|
+
expect(result.action).toBe('get-description');
|
|
152
|
+
expect(result.toolId).toBe('filesystem');
|
|
153
|
+
expect(result.output).toContain('FILESYSTEM TOOL');
|
|
154
|
+
expect(result.output).toContain('read, write');
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test('should return error for unknown tool', async () => {
|
|
158
|
+
const mockRegistry = {
|
|
159
|
+
getTool: jest.fn().mockReturnValue(null),
|
|
160
|
+
listTools: jest.fn().mockReturnValue(['filesystem', 'terminal'])
|
|
161
|
+
};
|
|
162
|
+
tool.setToolsRegistry(mockRegistry);
|
|
163
|
+
|
|
164
|
+
const result = await tool.execute({ tool: 'nonexistent' });
|
|
165
|
+
expect(result.success).toBe(false);
|
|
166
|
+
expect(result.error).toContain('Tool not found');
|
|
167
|
+
expect(result.output).toContain('nonexistent');
|
|
168
|
+
expect(result.output).toContain('filesystem, terminal');
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
test('should handle nested parameters format', async () => {
|
|
172
|
+
const mockTool = {
|
|
173
|
+
getDescription: jest.fn().mockReturnValue('desc'),
|
|
174
|
+
getCapabilities: jest.fn().mockReturnValue({
|
|
175
|
+
supportedActions: ['execute'],
|
|
176
|
+
async: false,
|
|
177
|
+
requiresProject: false
|
|
178
|
+
})
|
|
179
|
+
};
|
|
180
|
+
const mockRegistry = {
|
|
181
|
+
getTool: jest.fn().mockReturnValue(mockTool),
|
|
182
|
+
listTools: jest.fn()
|
|
183
|
+
};
|
|
184
|
+
tool.setToolsRegistry(mockRegistry);
|
|
185
|
+
|
|
186
|
+
const result = await tool.execute({ parameters: { tool: 'terminal' } });
|
|
187
|
+
expect(result.success).toBe(true);
|
|
188
|
+
expect(mockRegistry.getTool).toHaveBeenCalledWith('terminal');
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
test('should handle nested list parameter', async () => {
|
|
192
|
+
const mockRegistry = {
|
|
193
|
+
getTool: jest.fn().mockReturnValue({ isEnabled: true }),
|
|
194
|
+
listTools: jest.fn().mockReturnValue([]),
|
|
195
|
+
toolSummaries: new Map()
|
|
196
|
+
};
|
|
197
|
+
tool.setToolsRegistry(mockRegistry);
|
|
198
|
+
|
|
199
|
+
const result = await tool.execute({ parameters: { list: true } });
|
|
200
|
+
expect(result.success).toBe(true);
|
|
201
|
+
expect(result.action).toBe('list-tools');
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
});
|