UNPKG

osc-mcp-server

Version:

Model Context Protocol server for OSC (Open Sound Control) endpoint management

341 lines 13.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const server_1 = require("../server"); const manager_1 = require("../osc/manager"); jest.mock('@modelcontextprotocol/sdk/server/index.js', () => ({ Server: jest.fn(), })); jest.mock('@modelcontextprotocol/sdk/server/stdio.js', () => ({ StdioServerTransport: jest.fn(), })); jest.mock('@modelcontextprotocol/sdk/types.js', () => ({ CallToolRequestSchema: { method: 'tools/call' }, ListToolsRequestSchema: { method: 'tools/list' }, ErrorCode: { InvalidParams: 'InvalidParams', InternalError: 'InternalError', MethodNotFound: 'MethodNotFound', }, McpError: class McpError extends Error { code; details; constructor(code, message, details) { super(message); this.code = code; this.details = details; } }, })); describe('Error Handling Integration Tests', () => { let server; let mockMCPServer; let mockTransport; let callToolHandler; beforeEach(() => { jest.clearAllMocks(); mockMCPServer = { connect: jest.fn().mockResolvedValue(undefined), close: jest.fn().mockResolvedValue(undefined), setRequestHandler: jest.fn(), }; mockTransport = {}; const { Server } = require('@modelcontextprotocol/sdk/server/index.js'); const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js'); Server.mockImplementation(() => mockMCPServer); StdioServerTransport.mockImplementation(() => mockTransport); server = new server_1.OSCMCPServer(); const calls = mockMCPServer.setRequestHandler.mock.calls; const callToolCall = calls.find((call) => call[0].method === 'tools/call'); callToolHandler = callToolCall[1]; }); describe('Parameter Validation Integration', () => { it('should handle invalid port validation end-to-end', async () => { const request = { params: { name: 'create_osc_endpoint', arguments: { port: 100 }, }, }; try { await callToolHandler(request); fail('Expected error to be thrown'); } catch (error) { expect(error.code).toBe('InvalidParams'); expect(error.message).toContain("Parameter 'port' value 100 is out of range"); expect(error.details).toBeDefined(); } }); it('should handle missing required parameters', async () => { const request = { params: { name: 'create_osc_endpoint', arguments: {}, }, }; try { await callToolHandler(request); fail('Expected error to be thrown'); } catch (error) { expect(error.code).toBe('InvalidParams'); expect(error.message).toContain("Missing required parameter: 'port'"); } }); it('should handle invalid address filters', async () => { const request = { params: { name: 'create_osc_endpoint', arguments: { port: 8000, addressFilters: ['invalid-pattern'], }, }, }; try { await callToolHandler(request); fail('Expected error to be thrown'); } catch (error) { expect(error.code).toBe('InvalidParams'); expect(error.message).toContain('Invalid OSC address pattern'); } }); it('should handle invalid endpoint ID for stop operation', async () => { const request = { params: { name: 'stop_osc_endpoint', arguments: { endpointId: 123 }, }, }; try { await callToolHandler(request); fail('Expected error to be thrown'); } catch (error) { expect(error.code).toBe('InvalidParams'); expect(error.message).toContain("Invalid parameter 'endpointId': expected string"); } }); }); describe('Network Error Integration', () => { it('should handle port in use errors through the full stack', async () => { const firstRequest = { params: { name: 'create_osc_endpoint', arguments: { port: 8000 }, }, }; const firstResult = await callToolHandler(firstRequest); const firstResponse = JSON.parse(firstResult.content[0].text); expect(firstResponse.status).toBe('active'); const secondRequest = { params: { name: 'create_osc_endpoint', arguments: { port: 8000 }, }, }; try { await callToolHandler(secondRequest); fail('Expected error to be thrown'); } catch (error) { expect(error.code).toBe('InternalError'); expect(error.message).toContain('Port 8000 is already in use'); } }); }); describe('Endpoint Management Error Integration', () => { it('should handle stopping non-existent endpoints', async () => { const request = { params: { name: 'stop_osc_endpoint', arguments: { endpointId: 'non-existent' }, }, }; const result = await callToolHandler(request); const response = JSON.parse(result.content[0].text); expect(response.message).toContain("Endpoint 'non-existent' not found"); }); it('should handle querying messages from non-existent endpoints', async () => { const request = { params: { name: 'get_osc_messages', arguments: { endpointId: 'non-existent' }, }, }; const result = await callToolHandler(request); const response = JSON.parse(result.content[0].text); expect(response.messages).toEqual([]); expect(response.totalCount).toBe(0); }); }); describe('Complex Validation Scenarios', () => { it('should validate complex get_messages parameters', async () => { const invalidRequests = [ { arguments: { timeWindowSeconds: 'invalid' }, }, { arguments: { timeWindowSeconds: 100000 }, }, { arguments: { limit: 'invalid' }, }, { arguments: { limit: 2000 }, }, { arguments: { addressPattern: 'invalid' }, }, ]; for (const args of invalidRequests) { const request = { params: { name: 'get_osc_messages', arguments: args.arguments, }, }; try { await callToolHandler(request); fail(`Expected error for arguments: ${JSON.stringify(args.arguments)}`); } catch (error) { expect(error.code).toBe('InvalidParams'); } } }); it('should validate buffer size limits', async () => { const invalidRequests = [ { port: 8000, bufferSize: 0 }, { port: 8001, bufferSize: 20000 }, { port: 8002, bufferSize: 'invalid' }, ]; for (const args of invalidRequests) { const request = { params: { name: 'create_osc_endpoint', arguments: args, }, }; try { await callToolHandler(request); fail(`Expected error for arguments: ${JSON.stringify(args)}`); } catch (error) { expect(error.code).toBe('InvalidParams'); } } }); }); describe('Error Recovery and Suggestions', () => { it('should provide helpful error messages with suggestions', async () => { const request = { params: { name: 'create_osc_endpoint', arguments: { port: 100 }, }, }; try { await callToolHandler(request); fail('Expected error to be thrown'); } catch (error) { expect(error.message).toContain("Parameter 'port' value 100 is out of range"); expect(error.message).toContain('Must be between 1024 and 65535'); expect(error.details).toBeDefined(); expect(error.details.validRange).toEqual({ min: 1024, max: 65535 }); } }); it('should handle permission denied scenarios gracefully', async () => { const oscManager = (0, manager_1.createOSCManager)(); jest.spyOn(oscManager, 'createEndpoint').mockResolvedValue({ endpointId: '', port: 80, status: 'error', message: 'Permission denied to bind to port 80. Try using a port number above 1024 or run with appropriate privileges.', }); const response = await oscManager.createEndpoint({ port: 80 }); expect(response.status).toBe('error'); expect(response.message).toContain('Permission denied'); expect(response.message).toContain('Try using a port number above 1024'); }); }); describe('Tool Error Handling', () => { it('should handle unknown tool names gracefully', async () => { const request = { params: { name: 'unknown_tool', arguments: {}, }, }; try { await callToolHandler(request); fail('Expected error to be thrown'); } catch (error) { expect(error.code).toBe('MethodNotFound'); expect(error.message).toContain('Unknown tool: unknown_tool'); } }); it('should convert unexpected errors to proper MCP errors', async () => { const oscManager = server.getOSCManager(); jest .spyOn(oscManager, 'createEndpoint') .mockRejectedValue(new Error('Unexpected system error')); const request = { params: { name: 'create_osc_endpoint', arguments: { port: 8000 }, }, }; try { await callToolHandler(request); fail('Expected error to be thrown'); } catch (error) { expect(error.code).toBe('InternalError'); expect(error.message).toContain("Operation 'create_osc_endpoint' failed"); } }); }); describe('Concurrent Error Scenarios', () => { it('should handle multiple simultaneous endpoint creation attempts', async () => { const requests = Array.from({ length: 3 }, () => ({ params: { name: 'create_osc_endpoint', arguments: { port: 8000 }, }, })); const results = await Promise.allSettled(requests.map(req => callToolHandler(req))); let successCount = 0; let errorCount = 0; for (const result of results) { if (result.status === 'fulfilled') { const response = JSON.parse(result.value.content[0].text); if (response.status === 'active') { successCount++; } else if (response.status === 'error') { errorCount++; } } else { errorCount++; } } expect(successCount).toBeGreaterThanOrEqual(1); expect(errorCount).toBeGreaterThan(0); expect(successCount + errorCount).toBe(3); }); }); afterEach(async () => { try { const oscManager = server.getOSCManager(); await oscManager.shutdown(); } catch (_error) { } }); }); //# sourceMappingURL=integration.test.js.map