| |
| |
| |
| |
| |
|
|
| import { describe, it, expect, vi, beforeEach } from 'vitest'; |
| import { BaseToolInvocation, type ToolResult } from './tools.js'; |
| import type { MessageBus } from '../confirmation-bus/message-bus.js'; |
| import { |
| type Message, |
| MessageBusType, |
| type ToolConfirmationRequest, |
| type ToolConfirmationResponse, |
| } from '../confirmation-bus/types.js'; |
|
|
| class TestBaseToolInvocation extends BaseToolInvocation<object, ToolResult> { |
| getDescription(): string { |
| return 'test description'; |
| } |
| async execute(): Promise<ToolResult> { |
| return { llmContent: [], returnDisplay: '' }; |
| } |
| } |
|
|
| describe('BaseToolInvocation', () => { |
| let messageBus: MessageBus; |
| let abortController: AbortController; |
|
|
| beforeEach(() => { |
| messageBus = { |
| publish: vi.fn(), |
| subscribe: vi.fn(), |
| unsubscribe: vi.fn(), |
| } as unknown as MessageBus; |
| abortController = new AbortController(); |
| }); |
|
|
| it('should propagate serverName to ToolConfirmationRequest', async () => { |
| const serverName = 'test-server'; |
| const tool = new TestBaseToolInvocation( |
| {}, |
| messageBus, |
| 'test-tool', |
| 'Test Tool', |
| serverName, |
| ); |
|
|
| let capturedRequest: ToolConfirmationRequest | undefined; |
| vi.mocked(messageBus.publish).mockImplementation( |
| async (request: Message) => { |
| if (request.type === MessageBusType.TOOL_CONFIRMATION_REQUEST) { |
| capturedRequest = request; |
| } |
| }, |
| ); |
|
|
| let responseHandler: |
| | ((response: ToolConfirmationResponse) => void) |
| | undefined; |
| vi.mocked(messageBus.subscribe).mockImplementation( |
| (type: MessageBusType, handler: (message: Message) => void) => { |
| if (type === MessageBusType.TOOL_CONFIRMATION_RESPONSE) { |
| responseHandler = handler as ( |
| response: ToolConfirmationResponse, |
| ) => void; |
| } |
| }, |
| ); |
|
|
| const confirmationPromise = tool.shouldConfirmExecute( |
| abortController.signal, |
| ); |
|
|
| |
| await new Promise((resolve) => setTimeout(resolve, 0)); |
|
|
| expect(messageBus.publish).toHaveBeenCalledTimes(1); |
| expect(capturedRequest).toBeDefined(); |
| expect(capturedRequest?.type).toBe( |
| MessageBusType.TOOL_CONFIRMATION_REQUEST, |
| ); |
| expect(capturedRequest?.serverName).toBe(serverName); |
|
|
| |
| if (responseHandler && capturedRequest) { |
| responseHandler({ |
| type: MessageBusType.TOOL_CONFIRMATION_RESPONSE, |
| correlationId: capturedRequest.correlationId, |
| confirmed: true, |
| }); |
| } |
|
|
| await confirmationPromise; |
| }); |
|
|
| it('should NOT propagate serverName if not provided', async () => { |
| const tool = new TestBaseToolInvocation( |
| {}, |
| messageBus, |
| 'test-tool', |
| 'Test Tool', |
| |
| ); |
|
|
| let capturedRequest: ToolConfirmationRequest | undefined; |
| vi.mocked(messageBus.publish).mockImplementation( |
| async (request: Message) => { |
| if (request.type === MessageBusType.TOOL_CONFIRMATION_REQUEST) { |
| capturedRequest = request; |
| } |
| }, |
| ); |
|
|
| |
| |
| |
| const confirmationPromise = tool.shouldConfirmExecute( |
| abortController.signal, |
| ); |
|
|
| await new Promise((resolve) => setTimeout(resolve, 0)); |
|
|
| expect(messageBus.publish).toHaveBeenCalledTimes(1); |
| expect(capturedRequest).toBeDefined(); |
| expect(capturedRequest?.serverName).toBeUndefined(); |
|
|
| abortController.abort(); |
| try { |
| await confirmationPromise; |
| } catch { |
| |
| } |
| }); |
| }); |
|
|