quantum-cli-core
Version:
Quantum CLI Core - Multi-LLM Collaboration System
181 lines • 7.41 kB
JavaScript
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { executeToolCall } from './nonInteractiveToolExecutor.js';
import { Type } from '@google/genai';
const mockConfig = {
getSessionId: () => 'test-session-id',
getUsageStatisticsEnabled: () => true,
getDebugMode: () => false,
};
describe('executeToolCall', () => {
let mockToolRegistry;
let mockTool;
let abortController;
beforeEach(() => {
mockTool = {
name: 'testTool',
displayName: 'Test Tool',
description: 'A tool for testing',
schema: {
name: 'testTool',
description: 'A tool for testing',
parameters: {
type: Type.OBJECT,
properties: {
param1: { type: Type.STRING },
},
required: ['param1'],
},
},
execute: vi.fn(),
validateToolParams: vi.fn(() => null),
shouldConfirmExecute: vi.fn(() => Promise.resolve(false)),
isOutputMarkdown: false,
canUpdateOutput: false,
getDescription: vi.fn(),
};
mockToolRegistry = {
getTool: vi.fn(),
// Add other ToolRegistry methods if needed, or use a more complete mock
};
abortController = new AbortController();
});
it('should execute a tool successfully', async () => {
const request = {
callId: 'call1',
name: 'testTool',
args: { param1: 'value1' },
isClientInitiated: false,
};
const toolResult = {
llmContent: 'Tool executed successfully',
returnDisplay: 'Success!',
};
vi.mocked(mockToolRegistry.getTool).mockReturnValue(mockTool);
vi.mocked(mockTool.execute).mockResolvedValue(toolResult);
const response = await executeToolCall(mockConfig, request, mockToolRegistry, abortController.signal);
expect(mockToolRegistry.getTool).toHaveBeenCalledWith('testTool');
expect(mockTool.execute).toHaveBeenCalledWith(request.args, abortController.signal);
expect(response.callId).toBe('call1');
expect(response.error).toBeUndefined();
expect(response.resultDisplay).toBe('Success!');
expect(response.responseParts).toEqual({
functionResponse: {
name: 'testTool',
id: 'call1',
response: { output: 'Tool executed successfully' },
},
});
});
it('should return an error if tool is not found', async () => {
const request = {
callId: 'call2',
name: 'nonExistentTool',
args: {},
isClientInitiated: false,
};
vi.mocked(mockToolRegistry.getTool).mockReturnValue(undefined);
const response = await executeToolCall(mockConfig, request, mockToolRegistry, abortController.signal);
expect(response.callId).toBe('call2');
expect(response.error).toBeInstanceOf(Error);
expect(response.error?.message).toBe('Tool "nonExistentTool" not found in registry.');
expect(response.resultDisplay).toBe('Tool "nonExistentTool" not found in registry.');
expect(response.responseParts).toEqual([
{
functionResponse: {
name: 'nonExistentTool',
id: 'call2',
response: { error: 'Tool "nonExistentTool" not found in registry.' },
},
},
]);
});
it('should return an error if tool execution fails', async () => {
const request = {
callId: 'call3',
name: 'testTool',
args: { param1: 'value1' },
isClientInitiated: false,
};
const executionError = new Error('Tool execution failed');
vi.mocked(mockToolRegistry.getTool).mockReturnValue(mockTool);
vi.mocked(mockTool.execute).mockRejectedValue(executionError);
const response = await executeToolCall(mockConfig, request, mockToolRegistry, abortController.signal);
expect(response.callId).toBe('call3');
expect(response.error).toBe(executionError);
expect(response.resultDisplay).toBe('Tool execution failed');
expect(response.responseParts).toEqual([
{
functionResponse: {
name: 'testTool',
id: 'call3',
response: { error: 'Tool execution failed' },
},
},
]);
});
it('should handle cancellation during tool execution', async () => {
const request = {
callId: 'call4',
name: 'testTool',
args: { param1: 'value1' },
isClientInitiated: false,
};
const cancellationError = new Error('Operation cancelled');
vi.mocked(mockToolRegistry.getTool).mockReturnValue(mockTool);
vi.mocked(mockTool.execute).mockImplementation(async (_args, signal) => {
if (signal?.aborted) {
return Promise.reject(cancellationError);
}
return new Promise((_resolve, reject) => {
signal?.addEventListener('abort', () => {
reject(cancellationError);
});
// Simulate work that might happen if not aborted immediately
const timeoutId = setTimeout(() => reject(new Error('Should have been cancelled if not aborted prior')), 100);
signal?.addEventListener('abort', () => clearTimeout(timeoutId));
});
});
abortController.abort(); // Abort before calling
const response = await executeToolCall(mockConfig, request, mockToolRegistry, abortController.signal);
expect(response.callId).toBe('call4');
expect(response.error?.message).toBe(cancellationError.message);
expect(response.resultDisplay).toBe('Operation cancelled');
});
it('should correctly format llmContent with inlineData', async () => {
const request = {
callId: 'call5',
name: 'testTool',
args: {},
isClientInitiated: false,
};
const imageDataPart = {
inlineData: { mimeType: 'image/png', data: 'base64data' },
};
const toolResult = {
llmContent: [imageDataPart],
returnDisplay: 'Image processed',
};
vi.mocked(mockToolRegistry.getTool).mockReturnValue(mockTool);
vi.mocked(mockTool.execute).mockResolvedValue(toolResult);
const response = await executeToolCall(mockConfig, request, mockToolRegistry, abortController.signal);
expect(response.resultDisplay).toBe('Image processed');
expect(response.responseParts).toEqual([
{
functionResponse: {
name: 'testTool',
id: 'call5',
response: {
output: 'Binary content of type image/png was processed.',
},
},
},
imageDataPart,
]);
});
});
//# sourceMappingURL=nonInteractiveToolExecutor.test.js.map