use-prompt.test.js•13.2 kB
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { handleUsePrompt, usePromptToolDefinition } from '../../../tools/prompts/use-prompt.js';
import { promptRegistry } from '../../../handlers/prompts.js';
describe('Use Prompt Tool', () => {
    let mockServer;
    beforeEach(() => {
        // Clear registry before each test
        promptRegistry.clear();
        // Mock server
        mockServer = {
            createErrorResponse: vi.fn((error, context) => {
                throw new Error(`${context}: ${error.message}`);
            }),
        };
    });
    describe('Tool Definition', () => {
        it('should have correct tool definition', () => {
            expect(usePromptToolDefinition).toMatchObject({
                name: 'use_prompt',
                description: expect.stringContaining('Execute a registered prompt template'),
                inputSchema: {
                    type: 'object',
                    properties: {
                        prompt_name: {
                            type: 'string',
                            description: expect.stringContaining('prompt to execute'),
                        },
                        arguments: {
                            type: 'object',
                            description: expect.stringContaining('Arguments to pass'),
                        },
                    },
                    required: ['prompt_name'],
                },
            });
        });
    });
    describe('Functionality', () => {
        it('should execute prompt with no arguments', async () => {
            const mockMessages = [
                {
                    role: 'user',
                    content: {
                        type: 'text',
                        text: 'Test message',
                    },
                },
            ];
            promptRegistry.set('simple_prompt', {
                name: 'simple_prompt',
                title: 'Simple Prompt',
                description: 'A simple test prompt',
                arguments: [],
                handler: vi.fn().mockResolvedValue(mockMessages),
            });
            const result = await handleUsePrompt(mockServer, {
                prompt_name: 'simple_prompt',
            });
            expect(result).toHaveProperty('content');
            expect(result.content[0].type).toBe('text');
            const parsedContent = JSON.parse(result.content[0].text);
            expect(parsedContent).toHaveProperty('prompt_name', 'simple_prompt');
            expect(parsedContent).toHaveProperty('description', 'A simple test prompt');
            expect(parsedContent).toHaveProperty('messages', 1);
            expect(parsedContent).toHaveProperty('preview');
            // Verify handler was called with empty object
            expect(promptRegistry.get('simple_prompt').handler).toHaveBeenCalledWith({});
        });
        it('should execute prompt with arguments', async () => {
            const mockMessages = [
                {
                    role: 'system',
                    content: {
                        type: 'text',
                        text: 'Generated prompt for agent-123',
                    },
                },
            ];
            promptRegistry.set('agent_prompt', {
                name: 'agent_prompt',
                title: 'Agent Prompt',
                description: 'Prompt with parameters',
                arguments: [
                    { name: 'agent_id', required: true },
                    { name: 'message', required: false },
                ],
                handler: vi.fn().mockResolvedValue(mockMessages),
            });
            const args = {
                agent_id: 'agent-123',
                message: 'Hello',
            };
            const result = await handleUsePrompt(mockServer, {
                prompt_name: 'agent_prompt',
                arguments: args,
            });
            const parsedContent = JSON.parse(result.content[0].text);
            expect(parsedContent.messages).toBe(1);
            // Verify handler was called with correct arguments
            expect(promptRegistry.get('agent_prompt').handler).toHaveBeenCalledWith(args);
        });
        it('should handle prompts returning multiple messages', async () => {
            const multipleMessages = [
                {
                    role: 'system',
                    content: { type: 'text', text: 'System prompt' },
                },
                {
                    role: 'user',
                    content: { type: 'text', text: 'User message' },
                },
                {
                    role: 'assistant',
                    content: { type: 'text', text: 'Assistant response' },
                },
            ];
            promptRegistry.set('multi_message', {
                name: 'multi_message',
                description: 'Multi-message prompt',
                handler: vi.fn().mockResolvedValue(multipleMessages),
            });
            const result = await handleUsePrompt(mockServer, {
                prompt_name: 'multi_message',
            });
            const parsedContent = JSON.parse(result.content[0].text);
            expect(parsedContent.messages).toBe(3);
        });
        it('should include structuredContent in response', async () => {
            const messages = [{ role: 'user', content: { type: 'text', text: 'Test' } }];
            promptRegistry.set('test', {
                name: 'test',
                description: 'Test prompt',
                handler: vi.fn().mockResolvedValue(messages),
            });
            const result = await handleUsePrompt(mockServer, { prompt_name: 'test' });
            expect(result.structuredContent).toEqual({
                prompt_name: 'test',
                description: 'Test prompt',
                messages,
            });
        });
        it('should handle preview of long messages', async () => {
            const longText = 'A'.repeat(300);
            const messages = [
                {
                    role: 'user',
                    content: { type: 'text', text: longText },
                },
            ];
            promptRegistry.set('long_prompt', {
                name: 'long_prompt',
                description: 'Long prompt',
                handler: vi.fn().mockResolvedValue(messages),
            });
            const result = await handleUsePrompt(mockServer, { prompt_name: 'long_prompt' });
            const parsedContent = JSON.parse(result.content[0].text);
            expect(parsedContent.preview.endsWith('...')).toBe(true);
            expect(parsedContent.preview.length).toBeLessThan(210);
        });
    });
    describe('Error Handling', () => {
        it('should throw error when prompt_name is missing', async () => {
            await expect(handleUsePrompt(mockServer, {})).rejects.toThrow(
                'Failed to execute prompt: Missing required parameter: prompt_name',
            );
        });
        it('should throw error when prompt not found', async () => {
            await expect(
                handleUsePrompt(mockServer, { prompt_name: 'non_existent' }),
            ).rejects.toThrow('Failed to execute prompt: Prompt not found: non_existent');
        });
        it('should include available prompts in error message', async () => {
            promptRegistry.set('prompt1', { name: 'prompt1', handler: async () => [] });
            promptRegistry.set('prompt2', { name: 'prompt2', handler: async () => [] });
            await expect(
                handleUsePrompt(mockServer, { prompt_name: 'non_existent' }),
            ).rejects.toThrow('Available prompts: prompt1, prompt2');
        });
        it('should handle prompt handler errors', async () => {
            promptRegistry.set('error_prompt', {
                name: 'error_prompt',
                description: 'Error prompt',
                handler: vi.fn().mockRejectedValue(new Error('Handler failed')),
            });
            await expect(
                handleUsePrompt(mockServer, { prompt_name: 'error_prompt' }),
            ).rejects.toThrow('Failed to execute prompt: Handler failed');
        });
        it('should handle invalid prompt return value', async () => {
            promptRegistry.set('invalid_prompt', {
                name: 'invalid_prompt',
                description: 'Invalid prompt',
                handler: vi.fn().mockResolvedValue(null), // Invalid return
            });
            // Should throw error due to null.length
            await expect(
                handleUsePrompt(mockServer, { prompt_name: 'invalid_prompt' }),
            ).rejects.toThrow('Failed to execute prompt: Cannot read properties of null');
        });
        it('should handle prompt without description', async () => {
            promptRegistry.set('no_desc', {
                name: 'no_desc',
                // No description
                handler: vi.fn().mockResolvedValue([]),
            });
            const result = await handleUsePrompt(mockServer, { prompt_name: 'no_desc' });
            const parsedContent = JSON.parse(result.content[0].text);
            expect(parsedContent.description).toBeUndefined();
        });
    });
    describe('MCP Protocol Integration', () => {
        it('should call handler through MCP protocol', async () => {
            const mockHandler = vi
                .fn()
                .mockResolvedValue([{ role: 'user', content: { type: 'text', text: 'Test' } }]);
            promptRegistry.set('mcp_test', {
                name: 'mcp_test',
                description: 'MCP test prompt',
                handler: mockHandler,
            });
            // Simulate MCP protocol call
            const mcpRequest = {
                method: 'prompts/get',
                params: {
                    prompt_name: 'mcp_test',
                    arguments: { key: 'value' },
                },
            };
            await handleUsePrompt(mockServer, mcpRequest.params);
            expect(mockHandler).toHaveBeenCalledWith({ key: 'value' });
        });
        it('should handle undefined arguments as empty object', async () => {
            const mockHandler = vi.fn().mockResolvedValue([]);
            promptRegistry.set('test', {
                name: 'test',
                handler: mockHandler,
            });
            await handleUsePrompt(mockServer, { prompt_name: 'test' });
            expect(mockHandler).toHaveBeenCalledWith({});
        });
    });
    describe('Output Format', () => {
        it('should format JSON output with proper indentation', async () => {
            promptRegistry.set('format_test', {
                name: 'format_test',
                description: 'Format test',
                handler: vi
                    .fn()
                    .mockResolvedValue([{ role: 'user', content: { type: 'text', text: 'Test' } }]),
            });
            const result = await handleUsePrompt(mockServer, { prompt_name: 'format_test' });
            // Check for pretty printing
            expect(result.content[0].text).toContain('\n  ');
            expect(result.content[0].text).toContain('"prompt_name": "format_test"');
        });
        it('should handle complex message structures', async () => {
            const complexMessages = [
                {
                    role: 'user',
                    content: {
                        type: 'text',
                        text: 'Complex message',
                        metadata: {
                            timestamp: '2024-01-01',
                            source: 'test',
                        },
                    },
                },
                {
                    role: 'assistant',
                    content: {
                        type: 'resource',
                        resource: {
                            uri: 'file:///test.txt',
                            text: 'File content',
                            mimeType: 'text/plain',
                        },
                    },
                },
            ];
            promptRegistry.set('complex', {
                name: 'complex',
                description: 'Complex prompt',
                handler: vi.fn().mockResolvedValue(complexMessages),
            });
            const result = await handleUsePrompt(mockServer, { prompt_name: 'complex' });
            const parsedContent = JSON.parse(result.content[0].text);
            expect(parsedContent.messages).toBe(2);
        });
        it('should handle empty messages array', async () => {
            promptRegistry.set('empty', {
                name: 'empty',
                description: 'Empty prompt',
                handler: vi.fn().mockResolvedValue([]),
            });
            const result = await handleUsePrompt(mockServer, { prompt_name: 'empty' });
            const parsedContent = JSON.parse(result.content[0].text);
            expect(parsedContent.messages).toBe(0);
            expect(parsedContent.preview).toBe('undefined...');
        });
    });
});