From e405346b3edee3008bbb98ca35071e0aaa4d0601 Mon Sep 17 00:00:00 2001 From: czlonkowski <56956555+czlonkowski@users.noreply.github.com> Date: Tue, 29 Jul 2025 18:09:03 +0200 Subject: [PATCH] fix: resolve all TypeScript and lint errors in integration tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fixed InMemoryTransport destructuring (object → array) - Updated all callTool calls to new object syntax - Changed getServerInfo() to getServerVersion() - Added type assertions for response objects - Fixed import paths and missing imports - Corrected template and performance test type issues - All 56 TypeScript errors resolved Both 'npm run lint' and 'npm run typecheck' now pass successfully --- .../integration/database/performance.test.ts | 84 ++++--- .../database/template-repository.test.ts | 81 +++---- tests/integration/database/test-utils.ts | 13 +- .../mcp-protocol/error-handling.test.ts | 146 ++++++------ .../mcp-protocol/performance.test.ts | 64 +++--- .../mcp-protocol/protocol-compliance.test.ts | 88 ++++---- .../mcp-protocol/session-management.test.ts | 110 ++++----- .../integration/mcp-protocol/test-helpers.ts | 27 ++- .../mcp-protocol/tool-invocation.test.ts | 208 +++++++++--------- tests/integration/setup/msw-test-server.ts | 2 +- tests/mocks/n8n-api/index.ts | 2 +- tests/setup/msw-setup.ts | 4 +- 12 files changed, 435 insertions(+), 394 deletions(-) diff --git a/tests/integration/database/performance.test.ts b/tests/integration/database/performance.test.ts index 3621786..c71299a 100644 --- a/tests/integration/database/performance.test.ts +++ b/tests/integration/database/performance.test.ts @@ -5,6 +5,7 @@ import { TemplateRepository } from '../../../src/templates/template-repository'; import { DatabaseAdapter } from '../../../src/database/database-adapter'; import { TestDatabase, TestDataGenerator, PerformanceMonitor, createTestDatabaseAdapter } from './test-utils'; import { ParsedNode } from '../../../src/parsers/node-parser'; +import { TemplateWorkflow, TemplateDetail } from '../../../src/templates/template-fetcher'; describe('Database Performance Tests', () => { let testDb: TestDatabase; @@ -152,7 +153,21 @@ describe('Database Performance Tests', () => { const stop1 = monitor.start('insert_templates_with_fts'); const transaction = db.transaction((templates: any[]) => { - templates.forEach(t => templateRepo.saveTemplate(t)); + templates.forEach(t => { + const detail: TemplateDetail = { + id: t.id, + name: t.name, + description: t.description || '', + views: t.totalViews, + createdAt: t.createdAt, + workflow: { + nodes: [], + connections: {}, + settings: {} + } + }; + templateRepo.saveTemplate(t, detail); + }); }); transaction(templates); stop1(); @@ -192,36 +207,53 @@ describe('Database Performance Tests', () => { 'n8n-nodes-base.mongodb' ]; - const templates = Array.from({ length: 5000 }, (_, i) => ({ - id: i + 1, - name: `Template ${i}`, - workflow: { - nodes: Array.from({ length: 3 }, (_, j) => ({ - id: `node${j}`, - name: `Node ${j}`, - type: nodeTypes[(i + j) % nodeTypes.length], - typeVersion: 1, - position: [100 * j, 100], - parameters: {} - })), - connections: {}, - settings: {} - }, - user: { username: 'user' }, - views: 100, - totalViews: 100, - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString() - })); + const templates = Array.from({ length: 5000 }, (_, i) => { + const workflow: TemplateWorkflow = { + id: i + 1, + name: `Template ${i}`, + description: `Template description ${i}`, + totalViews: 100, + createdAt: new Date().toISOString(), + user: { + id: 1, + name: 'Test User', + username: 'user', + verified: false + }, + nodes: [] + }; + + const detail: TemplateDetail = { + id: i + 1, + name: `Template ${i}`, + description: `Template description ${i}`, + views: 100, + createdAt: new Date().toISOString(), + workflow: { + nodes: Array.from({ length: 3 }, (_, j) => ({ + id: `node${j}`, + name: `Node ${j}`, + type: nodeTypes[(i + j) % nodeTypes.length], + typeVersion: 1, + position: [100 * j, 100], + parameters: {} + })), + connections: {}, + settings: {} + } + }; + + return { workflow, detail }; + }); - const insertTransaction = db.transaction((templates: any[]) => { - templates.forEach(t => templateRepo.saveTemplate(t)); + const insertTransaction = db.transaction((items: any[]) => { + items.forEach(({ workflow, detail }) => templateRepo.saveTemplate(workflow, detail)); }); insertTransaction(templates); // Test searching by node types const stop = monitor.start('search_by_node_types'); - const results = templateRepo.getTemplatesByNodeTypes([ + const results = templateRepo.getTemplatesByNodes([ 'n8n-nodes-base.webhook', 'n8n-nodes-base.slack' ], 100); @@ -397,7 +429,7 @@ function generateNodes(count: number, startId: number = 0): ParsedNode[] { isWebhook: i % 5 === 0, isVersioned: true, version: '1', - documentation: i % 3 === 0 ? `Documentation for node ${i}` : null, + documentation: i % 3 === 0 ? `Documentation for node ${i}` : undefined, properties: Array.from({ length: 5 }, (_, j) => ({ displayName: `Property ${j}`, name: `prop${j}`, diff --git a/tests/integration/database/template-repository.test.ts b/tests/integration/database/template-repository.test.ts index 71668b7..8ebf79c 100644 --- a/tests/integration/database/template-repository.test.ts +++ b/tests/integration/database/template-repository.test.ts @@ -55,38 +55,39 @@ describe('TemplateRepository Integration Tests', () => { it('should handle templates with complex node types', () => { const template = createTemplateWorkflow({ - id: 1, - nodes: [ - { - id: 'node1', - name: 'Webhook', - type: 'n8n-nodes-base.webhook', - typeVersion: 1, - position: [100, 100], - parameters: {} - }, - { - id: 'node2', - name: 'HTTP Request', - type: 'n8n-nodes-base.httpRequest', - typeVersion: 3, - position: [300, 100], - parameters: { - url: 'https://api.example.com', - method: 'POST' - } - } - ] + id: 1 }); + const nodes = [ + { + id: 'node1', + name: 'Webhook', + type: 'n8n-nodes-base.webhook', + typeVersion: 1, + position: [100, 100], + parameters: {} + }, + { + id: 'node2', + name: 'HTTP Request', + type: 'n8n-nodes-base.httpRequest', + typeVersion: 3, + position: [300, 100], + parameters: { + url: 'https://api.example.com', + method: 'POST' + } + } + ]; + const detail = createTemplateDetail({ id: template.id, workflow: { id: template.id.toString(), name: template.name, - nodes: template.workflow.nodes, - connections: template.workflow.connections, - settings: template.workflow.settings + nodes: nodes, + connections: {}, + settings: {} } }); repository.saveTemplate(template, detail); @@ -101,13 +102,7 @@ describe('TemplateRepository Integration Tests', () => { it('should sanitize workflow data before saving', () => { const template = createTemplateWorkflow({ - workflowInfo: { - nodeCount: 5, - webhookCount: 1, - // Add some data that should be sanitized - executionId: 'should-be-removed', - pinData: { node1: { data: 'sensitive' } } - } + id: 5 }); const detail = createTemplateDetail({ @@ -115,9 +110,20 @@ describe('TemplateRepository Integration Tests', () => { workflow: { id: template.id.toString(), name: template.name, - nodes: template.workflow.nodes, - connections: template.workflow.connections, - settings: template.workflow.settings + nodes: [ + { + id: 'node1', + name: 'Start', + type: 'n8n-nodes-base.start', + typeVersion: 1, + position: [100, 100], + parameters: {} + } + ], + connections: {}, + settings: {}, + pinData: { node1: { data: 'sensitive' } }, + executionId: 'should-be-removed' } }); repository.saveTemplate(template, detail); @@ -502,9 +508,6 @@ function createTemplateDetail(overrides: any = {}): TemplateDetail { connections: overrides.connections || {}, settings: overrides.settings || {}, pinData: overrides.pinData - }, - categories: overrides.categories || [ - { id: 1, name: 'automation' } - ] + } }; } \ No newline at end of file diff --git a/tests/integration/database/test-utils.ts b/tests/integration/database/test-utils.ts index 83a2859..a16bf3c 100644 --- a/tests/integration/database/test-utils.ts +++ b/tests/integration/database/test-utils.ts @@ -2,6 +2,7 @@ import * as fs from 'fs'; import * as path from 'path'; import Database from 'better-sqlite3'; import { execSync } from 'child_process'; +import type { DatabaseAdapter } from '../../../src/database/database-adapter'; export interface TestDatabaseOptions { mode: 'memory' | 'file'; @@ -274,7 +275,7 @@ export function checkDatabaseIntegrity(db: Database.Database): { try { // Run integrity check - const result = db.prepare('PRAGMA integrity_check').all(); + const result = db.prepare('PRAGMA integrity_check').all() as Array<{ integrity_check: string }>; if (result.length !== 1 || result[0].integrity_check !== 'ok') { errors.push('Database integrity check failed'); } @@ -315,10 +316,12 @@ export function createTestDatabaseAdapter(db: Database.Database): DatabaseAdapte get: (...params: any[]) => stmt.get(...params), all: (...params: any[]) => stmt.all(...params), iterate: (...params: any[]) => stmt.iterate(...params), - pluck: (enabled?: boolean) => stmt.pluck(enabled), - finalize: () => stmt, - bind: (...params: any[]) => stmt.bind(...params) - }; + pluck: function(enabled?: boolean) { stmt.pluck(enabled); return this; }, + expand: function(enabled?: boolean) { stmt.expand?.(enabled); return this; }, + raw: function(enabled?: boolean) { stmt.raw?.(enabled); return this; }, + columns: () => stmt.columns?.() || [], + bind: function(...params: any[]) { stmt.bind(...params); return this; } + } as any; }, exec: (sql: string) => db.exec(sql), close: () => db.close(), diff --git a/tests/integration/mcp-protocol/error-handling.test.ts b/tests/integration/mcp-protocol/error-handling.test.ts index 2b0cc40..4d78c66 100644 --- a/tests/integration/mcp-protocol/error-handling.test.ts +++ b/tests/integration/mcp-protocol/error-handling.test.ts @@ -11,7 +11,7 @@ describe('MCP Error Handling', () => { mcpServer = new TestableN8NMCPServer(); await mcpServer.initialize(); - const { serverTransport, clientTransport } = InMemoryTransport.createLinkedPair(); + const [serverTransport, clientTransport] = InMemoryTransport.createLinkedPair(); await mcpServer.connectToTransport(serverTransport); client = new Client({ @@ -33,7 +33,7 @@ describe('MCP Error Handling', () => { it('should handle invalid request (parse error)', async () => { // The MCP SDK handles parsing, so we test with invalid method instead try { - await client.request({ + await (client as any).request({ method: '', // Empty method params: {} }); @@ -45,7 +45,7 @@ describe('MCP Error Handling', () => { it('should handle method not found', async () => { try { - await client.request({ + await (client as any).request({ method: 'nonexistent/method', params: {} }); @@ -59,7 +59,7 @@ describe('MCP Error Handling', () => { it('should handle invalid params', async () => { try { // Missing required parameter - await client.callTool('get_node_info', {}); + await client.callTool({ name: 'get_node_info', arguments: {} }); expect.fail('Should have thrown an error'); } catch (error: any) { expect(error).toBeDefined(); @@ -70,9 +70,9 @@ describe('MCP Error Handling', () => { it('should handle internal errors gracefully', async () => { try { // Invalid node type format should cause internal processing error - await client.callTool('get_node_info', { + await client.callTool({ name: 'get_node_info', arguments: { nodeType: 'completely-invalid-format-$$$$' - }); + } }); expect.fail('Should have thrown an error'); } catch (error: any) { expect(error).toBeDefined(); @@ -84,22 +84,22 @@ describe('MCP Error Handling', () => { describe('Tool-Specific Errors', () => { describe('Node Discovery Errors', () => { it('should handle invalid category filter', async () => { - const response = await client.callTool('list_nodes', { + const response = await client.callTool({ name: 'list_nodes', arguments: { category: 'invalid_category' - }); + } }); // Should return empty array, not error - const nodes = JSON.parse(response[0].text); + const nodes = JSON.parse((response as any)[0].text); expect(Array.isArray(nodes)).toBe(true); expect(nodes).toHaveLength(0); }); it('should handle invalid search mode', async () => { try { - await client.callTool('search_nodes', { + await client.callTool({ name: 'search_nodes', arguments: { query: 'test', mode: 'INVALID_MODE' as any - }); + } }); expect.fail('Should have thrown an error'); } catch (error: any) { expect(error).toBeDefined(); @@ -108,9 +108,9 @@ describe('MCP Error Handling', () => { it('should handle empty search query', async () => { try { - await client.callTool('search_nodes', { + await client.callTool({ name: 'search_nodes', arguments: { query: '' - }); + } }); expect.fail('Should have thrown an error'); } catch (error: any) { expect(error).toBeDefined(); @@ -120,9 +120,9 @@ describe('MCP Error Handling', () => { it('should handle non-existent node types', async () => { try { - await client.callTool('get_node_info', { + await client.callTool({ name: 'get_node_info', arguments: { nodeType: 'nodes-base.thisDoesNotExist' - }); + } }); expect.fail('Should have thrown an error'); } catch (error: any) { expect(error).toBeDefined(); @@ -134,11 +134,11 @@ describe('MCP Error Handling', () => { describe('Validation Errors', () => { it('should handle invalid validation profile', async () => { try { - await client.callTool('validate_node_operation', { + await client.callTool({ name: 'validate_node_operation', arguments: { nodeType: 'nodes-base.httpRequest', config: { method: 'GET', url: 'https://api.example.com' }, profile: 'invalid_profile' as any - }); + } }); expect.fail('Should have thrown an error'); } catch (error: any) { expect(error).toBeDefined(); @@ -147,12 +147,12 @@ describe('MCP Error Handling', () => { it('should handle malformed workflow structure', async () => { try { - await client.callTool('validate_workflow', { + await client.callTool({ name: 'validate_workflow', arguments: { workflow: { // Missing required 'nodes' array connections: {} } - }); + } }); expect.fail('Should have thrown an error'); } catch (error: any) { expect(error).toBeDefined(); @@ -190,29 +190,29 @@ describe('MCP Error Handling', () => { } }; - const response = await client.callTool('validate_workflow', { + const response = await client.callTool({ name: 'validate_workflow', arguments: { workflow - }); + } }); - const validation = JSON.parse(response[0].text); + const validation = JSON.parse((response as any)[0].text); expect(validation.warnings).toBeDefined(); }); }); describe('Documentation Errors', () => { it('should handle non-existent documentation topics', async () => { - const response = await client.callTool('tools_documentation', { + const response = await client.callTool({ name: 'tools_documentation', arguments: { topic: 'completely_fake_tool' - }); + } }); - expect(response[0].text).toContain('not found'); + expect((response as any)[0].text).toContain('not found'); }); it('should handle invalid depth parameter', async () => { try { - await client.callTool('tools_documentation', { + await client.callTool({ name: 'tools_documentation', arguments: { depth: 'invalid_depth' as any - }); + } }); expect.fail('Should have thrown an error'); } catch (error: any) { expect(error).toBeDefined(); @@ -224,14 +224,14 @@ describe('MCP Error Handling', () => { describe('Large Payload Handling', () => { it('should handle large node info requests', async () => { // HTTP Request node has extensive properties - const response = await client.callTool('get_node_info', { + const response = await client.callTool({ name: 'get_node_info', arguments: { nodeType: 'nodes-base.httpRequest' - }); + } }); - expect(response[0].text.length).toBeGreaterThan(10000); + expect((response as any)[0].text.length).toBeGreaterThan(10000); // Should be valid JSON - const nodeInfo = JSON.parse(response[0].text); + const nodeInfo = JSON.parse((response as any)[0].text); expect(nodeInfo).toHaveProperty('properties'); }); @@ -259,11 +259,11 @@ describe('MCP Error Handling', () => { } } - const response = await client.callTool('validate_workflow', { + const response = await client.callTool({ name: 'validate_workflow', arguments: { workflow: { nodes, connections } - }); + } }); - const validation = JSON.parse(response[0].text); + const validation = JSON.parse((response as any)[0].text); expect(validation).toHaveProperty('valid'); }); @@ -273,10 +273,10 @@ describe('MCP Error Handling', () => { for (let i = 0; i < requestCount; i++) { promises.push( - client.callTool('list_nodes', { + client.callTool({ name: 'list_nodes', arguments: { limit: 1, category: i % 2 === 0 ? 'trigger' : 'transform' - }) + } }) ); } @@ -289,10 +289,10 @@ describe('MCP Error Handling', () => { it('should handle invalid JSON in tool parameters', async () => { try { // Config should be an object, not a string - await client.callTool('validate_node_operation', { + await client.callTool({ name: 'validate_node_operation', arguments: { nodeType: 'nodes-base.httpRequest', config: 'invalid json string' as any - }); + } }); expect.fail('Should have thrown an error'); } catch (error: any) { expect(error).toBeDefined(); @@ -301,9 +301,9 @@ describe('MCP Error Handling', () => { it('should handle malformed workflow JSON', async () => { try { - await client.callTool('validate_workflow', { + await client.callTool({ name: 'validate_workflow', arguments: { workflow: 'not a valid workflow object' as any - }); + } }); expect.fail('Should have thrown an error'); } catch (error: any) { expect(error).toBeDefined(); @@ -316,7 +316,7 @@ describe('MCP Error Handling', () => { const start = Date.now(); for (let i = 0; i < 20; i++) { - await client.callTool('get_database_statistics', {}); + await client.callTool({ name: 'get_database_statistics', arguments: {} }); } const duration = Date.now() - start; @@ -327,10 +327,10 @@ describe('MCP Error Handling', () => { it('should handle long-running operations', async () => { // Search with complex query that requires more processing - const response = await client.callTool('search_nodes', { + const response = await client.callTool({ name: 'search_nodes', arguments: { query: 'a b c d e f g h i j k l m n o p q r s t u v w x y z', mode: 'AND' - }); + } }); expect(response).toBeDefined(); }); @@ -351,7 +351,7 @@ describe('MCP Error Handling', () => { for (const nodeType of largeNodes) { promises.push( - client.callTool('get_node_info', { nodeType }) + client.callTool({ name: 'get_node_info', arguments: { nodeType } }) .catch(() => null) // Some might not exist ); } @@ -380,14 +380,14 @@ describe('MCP Error Handling', () => { }); } - const response = await client.callTool('validate_workflow', { + const response = await client.callTool({ name: 'validate_workflow', arguments: { workflow: { nodes, connections: {} } - }); + } }); - const validation = JSON.parse(response[0].text); + const validation = JSON.parse((response as any)[0].text); expect(validation).toHaveProperty('valid'); }); }); @@ -396,25 +396,25 @@ describe('MCP Error Handling', () => { it('should continue working after errors', async () => { // Cause an error try { - await client.callTool('get_node_info', { + await client.callTool({ name: 'get_node_info', arguments: { nodeType: 'invalid' - }); + } }); } catch (error) { // Expected } // Should still work - const response = await client.callTool('list_nodes', { limit: 1 }); + const response = await client.callTool({ name: 'list_nodes', arguments: { limit: 1 } }); expect(response).toBeDefined(); }); it('should handle mixed success and failure', async () => { const promises = [ - client.callTool('list_nodes', { limit: 5 }), - client.callTool('get_node_info', { nodeType: 'invalid' }).catch(e => ({ error: e })), - client.callTool('get_database_statistics', {}), - client.callTool('search_nodes', { query: '' }).catch(e => ({ error: e })), - client.callTool('list_ai_tools', {}) + client.callTool({ name: 'list_nodes', arguments: { limit: 5 } }), + client.callTool({ name: 'get_node_info', arguments: { nodeType: 'invalid' } }).catch(e => ({ error: e })), + client.callTool({ name: 'get_database_statistics', arguments: {} }), + client.callTool({ name: 'search_nodes', arguments: { query: '' } }).catch(e => ({ error: e })), + client.callTool({ name: 'list_ai_tools', arguments: {} }) ]; const results = await Promise.all(promises); @@ -430,42 +430,42 @@ describe('MCP Error Handling', () => { describe('Edge Cases', () => { it('should handle empty responses gracefully', async () => { - const response = await client.callTool('list_nodes', { + const response = await client.callTool({ name: 'list_nodes', arguments: { category: 'nonexistent_category' - }); + } }); - const nodes = JSON.parse(response[0].text); + const nodes = JSON.parse((response as any)[0].text); expect(Array.isArray(nodes)).toBe(true); expect(nodes).toHaveLength(0); }); it('should handle special characters in parameters', async () => { - const response = await client.callTool('search_nodes', { + const response = await client.callTool({ name: 'search_nodes', arguments: { query: 'test!@#$%^&*()_+-=[]{}|;\':",./<>?' - }); + } }); // Should return results or empty array, not error - const nodes = JSON.parse(response[0].text); + const nodes = JSON.parse((response as any)[0].text); expect(Array.isArray(nodes)).toBe(true); }); it('should handle unicode in parameters', async () => { - const response = await client.callTool('search_nodes', { + const response = await client.callTool({ name: 'search_nodes', arguments: { query: 'test 测试 тест परीक्षण' - }); + } }); - const nodes = JSON.parse(response[0].text); + const nodes = JSON.parse((response as any)[0].text); expect(Array.isArray(nodes)).toBe(true); }); it('should handle null and undefined gracefully', async () => { // Most tools should handle missing optional params - const response = await client.callTool('list_nodes', { + const response = await client.callTool({ name: 'list_nodes', arguments: { limit: undefined as any, category: null as any - }); + } }); - const nodes = JSON.parse(response[0].text); + const nodes = JSON.parse((response as any)[0].text); expect(Array.isArray(nodes)).toBe(true); }); }); @@ -473,9 +473,9 @@ describe('MCP Error Handling', () => { describe('Error Message Quality', () => { it('should provide helpful error messages', async () => { try { - await client.callTool('get_node_info', { + await client.callTool({ name: 'get_node_info', arguments: { nodeType: 'httpRequest' // Missing prefix - }); + } }); expect.fail('Should have thrown an error'); } catch (error: any) { expect(error.message).toBeDefined(); @@ -487,7 +487,7 @@ describe('MCP Error Handling', () => { it('should indicate missing required parameters', async () => { try { - await client.callTool('search_nodes', {}); + await client.callTool({ name: 'search_nodes', arguments: {} }); expect.fail('Should have thrown an error'); } catch (error: any) { expect(error.message).toContain('query'); @@ -495,15 +495,15 @@ describe('MCP Error Handling', () => { }); it('should provide context for validation errors', async () => { - const response = await client.callTool('validate_node_operation', { + const response = await client.callTool({ name: 'validate_node_operation', arguments: { nodeType: 'nodes-base.httpRequest', config: { // Missing required fields method: 'INVALID_METHOD' } - }); + } }); - const validation = JSON.parse(response[0].text); + const validation = JSON.parse((response as any)[0].text); expect(validation.valid).toBe(false); expect(validation.errors[0].message).toBeDefined(); expect(validation.errors[0].field).toBeDefined(); diff --git a/tests/integration/mcp-protocol/performance.test.ts b/tests/integration/mcp-protocol/performance.test.ts index 3e65c94..176102d 100644 --- a/tests/integration/mcp-protocol/performance.test.ts +++ b/tests/integration/mcp-protocol/performance.test.ts @@ -11,7 +11,7 @@ describe('MCP Performance Tests', () => { mcpServer = new TestableN8NMCPServer(); await mcpServer.initialize(); - const { serverTransport, clientTransport } = InMemoryTransport.createLinkedPair(); + const [serverTransport, clientTransport] = InMemoryTransport.createLinkedPair(); await mcpServer.connectToTransport(serverTransport); client = new Client({ @@ -35,7 +35,7 @@ describe('MCP Performance Tests', () => { const start = performance.now(); for (let i = 0; i < iterations; i++) { - await client.callTool('get_database_statistics', {}); + await client.callTool({ name: 'get_database_statistics', arguments: {} }); } const duration = performance.now() - start; @@ -52,7 +52,7 @@ describe('MCP Performance Tests', () => { const start = performance.now(); for (let i = 0; i < iterations; i++) { - await client.callTool('list_nodes', { limit: 10 }); + await client.callTool({ name: 'list_nodes', arguments: { limit: 10 } }); } const duration = performance.now() - start; @@ -71,7 +71,7 @@ describe('MCP Performance Tests', () => { for (let i = 0; i < iterations; i++) { for (const query of searches) { - await client.callTool('search_nodes', { query }); + await client.callTool({ name: 'search_nodes', arguments: { query } }); } } @@ -97,7 +97,7 @@ describe('MCP Performance Tests', () => { const start = performance.now(); for (const nodeType of nodeTypes) { - await client.callTool('get_node_info', { nodeType }); + await client.callTool({ name: 'get_node_info', arguments: { nodeType } }); } const duration = performance.now() - start; @@ -118,7 +118,7 @@ describe('MCP Performance Tests', () => { const promises = []; for (let i = 0; i < concurrentRequests; i++) { promises.push( - client.callTool('list_nodes', { limit: 5 }) + client.callTool({ name: 'list_nodes', arguments: { limit: 5 } }) ); } @@ -147,7 +147,7 @@ describe('MCP Performance Tests', () => { for (let round = 0; round < rounds; round++) { const promises = operations.map(op => - client.callTool(op.tool, op.params) + client.callTool({ name: op.tool, arguments: op.params }) ); await Promise.all(promises); } @@ -166,9 +166,9 @@ describe('MCP Performance Tests', () => { it('should handle large node lists efficiently', async () => { const start = performance.now(); - const response = await client.callTool('list_nodes', { + const response = await client.callTool({ name: 'list_nodes', arguments: { limit: 200 // Get many nodes - }); + } }); const duration = performance.now() - start; @@ -177,7 +177,7 @@ describe('MCP Performance Tests', () => { // Should complete within 100ms expect(duration).toBeLessThan(100); - const nodes = JSON.parse(response[0].text); + const nodes = JSON.parse((response as any)[0].text); expect(nodes.length).toBeGreaterThan(100); }); @@ -208,9 +208,9 @@ describe('MCP Performance Tests', () => { const start = performance.now(); - const response = await client.callTool('validate_workflow', { + const response = await client.callTool({ name: 'validate_workflow', arguments: { workflow: { nodes, connections } - }); + } }); const duration = performance.now() - start; @@ -219,7 +219,7 @@ describe('MCP Performance Tests', () => { // Should complete within 500ms expect(duration).toBeLessThan(500); - const validation = JSON.parse(response[0].text); + const validation = JSON.parse((response as any)[0].text); expect(validation).toHaveProperty('valid'); }); }); @@ -237,7 +237,7 @@ describe('MCP Performance Tests', () => { for (let j = 0; j < batchSize; j++) { promises.push( - client.callTool('get_database_statistics', {}) + client.callTool({ name: 'get_database_statistics', arguments: {} }) ); } @@ -263,10 +263,10 @@ describe('MCP Performance Tests', () => { // Perform large operations for (let i = 0; i < 10; i++) { - await client.callTool('list_nodes', { limit: 200 }); - await client.callTool('get_node_info', { + await client.callTool({ name: 'list_nodes', arguments: { limit: 200 } }); + await client.callTool({ name: 'get_node_info', arguments: { nodeType: 'nodes-base.httpRequest' - }); + } }); } // Force garbage collection if available @@ -296,7 +296,7 @@ describe('MCP Performance Tests', () => { const promises = []; for (let i = 0; i < load; i++) { promises.push( - client.callTool('list_nodes', { limit: 1 }) + client.callTool({ name: 'list_nodes', arguments: { limit: 1 } }) ); } @@ -332,16 +332,16 @@ describe('MCP Performance Tests', () => { const operation = i % 4; switch (operation) { case 0: - promises.push(client.callTool('list_nodes', { limit: 5 })); + promises.push(client.callTool({ name: 'list_nodes', arguments: { limit: 5 } })); break; case 1: - promises.push(client.callTool('search_nodes', { query: 'test' })); + promises.push(client.callTool({ name: 'search_nodes', arguments: { query: 'test' } })); break; case 2: - promises.push(client.callTool('get_database_statistics', {})); + promises.push(client.callTool({ name: 'get_database_statistics', arguments: {} })); break; case 3: - promises.push(client.callTool('list_ai_tools', {})); + promises.push(client.callTool({ name: 'list_ai_tools', arguments: {} })); break; } } @@ -360,14 +360,14 @@ describe('MCP Performance Tests', () => { describe('Critical Path Optimization', () => { it('should optimize tool listing performance', async () => { // Warm up - await client.callTool('list_nodes', { limit: 1 }); + await client.callTool({ name: 'list_nodes', arguments: { limit: 1 } }); const iterations = 100; const times: number[] = []; for (let i = 0; i < iterations; i++) { const start = performance.now(); - await client.callTool('list_nodes', { limit: 20 }); + await client.callTool({ name: 'list_nodes', arguments: { limit: 20 } }); times.push(performance.now() - start); } @@ -386,7 +386,7 @@ describe('MCP Performance Tests', () => { it('should optimize search performance', async () => { // Warm up - await client.callTool('search_nodes', { query: 'test' }); + await client.callTool({ name: 'search_nodes', arguments: { query: 'test' } }); const queries = ['http', 'webhook', 'database', 'api', 'slack']; const times: number[] = []; @@ -394,7 +394,7 @@ describe('MCP Performance Tests', () => { for (const query of queries) { for (let i = 0; i < 20; i++) { const start = performance.now(); - await client.callTool('search_nodes', { query }); + await client.callTool({ name: 'search_nodes', arguments: { query } }); times.push(performance.now() - start); } } @@ -412,14 +412,14 @@ describe('MCP Performance Tests', () => { // First call (cold) const coldStart = performance.now(); - await client.callTool('get_node_info', { nodeType }); + await client.callTool({ name: 'get_node_info', arguments: { nodeType } }); const coldTime = performance.now() - coldStart; // Subsequent calls (potentially cached) const warmTimes: number[] = []; for (let i = 0; i < 10; i++) { const start = performance.now(); - await client.callTool('get_node_info', { nodeType }); + await client.callTool({ name: 'get_node_info', arguments: { nodeType } }); warmTimes.push(performance.now() - start); } @@ -441,7 +441,7 @@ describe('MCP Performance Tests', () => { while (performance.now() - start < duration) { try { - await client.callTool('get_database_statistics', {}); + await client.callTool({ name: 'get_database_statistics', arguments: {} }); requestCount++; } catch (error) { errorCount++; @@ -465,7 +465,7 @@ describe('MCP Performance Tests', () => { const heavyPromises = []; for (let i = 0; i < 200; i++) { heavyPromises.push( - client.callTool('validate_workflow', { + client.callTool({ name: 'validate_workflow', arguments: { workflow: { nodes: Array(20).fill(null).map((_, idx) => ({ id: String(idx), @@ -477,7 +477,7 @@ describe('MCP Performance Tests', () => { })), connections: {} } - }) + } }) ); } @@ -487,7 +487,7 @@ describe('MCP Performance Tests', () => { const recoveryTimes: number[] = []; for (let i = 0; i < 10; i++) { const start = performance.now(); - await client.callTool('get_database_statistics', {}); + await client.callTool({ name: 'get_database_statistics', arguments: {} }); recoveryTimes.push(performance.now() - start); } diff --git a/tests/integration/mcp-protocol/protocol-compliance.test.ts b/tests/integration/mcp-protocol/protocol-compliance.test.ts index 8ee14c3..73b65d7 100644 --- a/tests/integration/mcp-protocol/protocol-compliance.test.ts +++ b/tests/integration/mcp-protocol/protocol-compliance.test.ts @@ -12,7 +12,7 @@ describe('MCP Protocol Compliance', () => { mcpServer = new TestableN8NMCPServer(); await mcpServer.initialize(); - const { serverTransport, clientTransport } = InMemoryTransport.createLinkedPair(); + const [serverTransport, clientTransport] = InMemoryTransport.createLinkedPair(); transport = serverTransport; // Connect MCP server to transport @@ -36,18 +36,18 @@ describe('MCP Protocol Compliance', () => { describe('JSON-RPC 2.0 Compliance', () => { it('should return proper JSON-RPC 2.0 response format', async () => { - const response = await client.request({ + const response = await (client as any).request({ method: 'tools/list', params: {} }); // Response should have tools array expect(response).toHaveProperty('tools'); - expect(Array.isArray(response.tools)).toBe(true); + expect(Array.isArray((response as any).tools)).toBe(true); }); it('should handle request with id correctly', async () => { - const response = await client.request({ + const response = await (client as any).request({ method: 'tools/list', params: {} }); @@ -59,9 +59,9 @@ describe('MCP Protocol Compliance', () => { it('should handle batch requests', async () => { // Send multiple requests concurrently const promises = [ - client.request({ method: 'tools/list', params: {} }), - client.request({ method: 'tools/list', params: {} }), - client.request({ method: 'tools/list', params: {} }) + (client as any).request({ method: 'tools/list', params: {} }), + (client as any).request({ method: 'tools/list', params: {} }), + (client as any).request({ method: 'tools/list', params: {} }) ]; const responses = await Promise.all(promises); @@ -80,7 +80,7 @@ describe('MCP Protocol Compliance', () => { for (let i = 0; i < 5; i++) { expectedOrder.push(i); requests.push( - client.callTool('get_database_statistics', {}) + client.callTool({ name: 'get_database_statistics', arguments: {} }) .then(() => i) ); } @@ -92,18 +92,18 @@ describe('MCP Protocol Compliance', () => { describe('Protocol Version Negotiation', () => { it('should negotiate protocol capabilities', async () => { - const serverInfo = await client.getServerInfo(); + const serverInfo = await client.getServerVersion(); expect(serverInfo).toHaveProperty('name'); expect(serverInfo).toHaveProperty('version'); - expect(serverInfo.name).toBe('n8n-documentation-mcp'); + expect(serverInfo!.name).toBe('n8n-documentation-mcp'); }); it('should expose supported capabilities', async () => { - const serverInfo = await client.getServerInfo(); + const serverInfo = await client.getServerVersion(); expect(serverInfo).toHaveProperty('capabilities'); - const capabilities = serverInfo.capabilities || {}; + const capabilities = serverInfo!.capabilities || {}; // Should support tools expect(capabilities).toHaveProperty('tools'); @@ -121,7 +121,7 @@ describe('MCP Protocol Compliance', () => { try { // This should fail as MCP SDK validates method - await testClient.request({ method: '', params: {} }); + await (testClient as any).request({ method: '', params: {} }); expect.fail('Should have thrown an error'); } catch (error) { expect(error).toBeDefined(); @@ -132,16 +132,16 @@ describe('MCP Protocol Compliance', () => { it('should handle missing params gracefully', async () => { // Most tools should work without params - const response = await client.callTool('list_nodes', {}); + const response = await client.callTool({ name: 'list_nodes', arguments: {} }); expect(response).toBeDefined(); }); it('should validate params schema', async () => { try { // Invalid nodeType format (missing prefix) - await client.callTool('get_node_info', { + await client.callTool({ name: 'get_node_info', arguments: { nodeType: 'httpRequest' // Should be 'nodes-base.httpRequest' - }); + } }); expect.fail('Should have thrown an error'); } catch (error: any) { expect(error.message).toContain('not found'); @@ -151,32 +151,32 @@ describe('MCP Protocol Compliance', () => { describe('Content Types', () => { it('should handle text content in tool responses', async () => { - const response = await client.callTool('get_database_statistics', {}); + const response = await client.callTool({ name: 'get_database_statistics', arguments: {} }); expect(response).toHaveLength(1); expect(response[0]).toHaveProperty('type', 'text'); expect(response[0]).toHaveProperty('text'); - expect(typeof response[0].text).toBe('string'); + expect(typeof (response[0] as any).text).toBe('string'); }); it('should handle large text responses', async () => { // Get a large node info response - const response = await client.callTool('get_node_info', { + const response = await client.callTool({ name: 'get_node_info', arguments: { nodeType: 'nodes-base.httpRequest' - }); + } }); expect(response).toHaveLength(1); - expect(response[0].type).toBe('text'); - expect(response[0].text.length).toBeGreaterThan(1000); + expect((response[0] as any).type).toBe('text'); + expect((response[0] as any).text.length).toBeGreaterThan(1000); }); it('should handle JSON content properly', async () => { - const response = await client.callTool('list_nodes', { + const response = await client.callTool({ name: 'list_nodes', arguments: { limit: 5 - }); + } }); expect(response).toHaveLength(1); - const content = JSON.parse(response[0].text); + const content = JSON.parse((response[0] as any).text); expect(Array.isArray(content)).toBe(true); }); }); @@ -184,29 +184,29 @@ describe('MCP Protocol Compliance', () => { describe('Request/Response Correlation', () => { it('should correlate concurrent requests correctly', async () => { const requests = [ - client.callTool('get_node_essentials', { nodeType: 'nodes-base.httpRequest' }), - client.callTool('get_node_essentials', { nodeType: 'nodes-base.webhook' }), - client.callTool('get_node_essentials', { nodeType: 'nodes-base.slack' }) + client.callTool({ name: 'get_node_essentials', arguments: { nodeType: 'nodes-base.httpRequest' } }), + client.callTool({ name: 'get_node_essentials', arguments: { nodeType: 'nodes-base.webhook' } }), + client.callTool({ name: 'get_node_essentials', arguments: { nodeType: 'nodes-base.slack' } }) ]; const responses = await Promise.all(requests); - expect(responses[0][0].text).toContain('httpRequest'); - expect(responses[1][0].text).toContain('webhook'); - expect(responses[2][0].text).toContain('slack'); + expect((responses[0][0] as any).text).toContain('httpRequest'); + expect((responses[1][0] as any).text).toContain('webhook'); + expect((responses[2][0] as any).text).toContain('slack'); }); it('should handle interleaved requests', async () => { const results: string[] = []; // Start multiple requests with different delays - const p1 = client.callTool('get_database_statistics', {}) + const p1 = client.callTool({ name: 'get_database_statistics', arguments: {} }) .then(() => { results.push('stats'); return 'stats'; }); - const p2 = client.callTool('list_nodes', { limit: 1 }) + const p2 = client.callTool({ name: 'list_nodes', arguments: { limit: 1 } }) .then(() => { results.push('nodes'); return 'nodes'; }); - const p3 = client.callTool('search_nodes', { query: 'http' }) + const p3 = client.callTool({ name: 'search_nodes', arguments: { query: 'http' } }) .then(() => { results.push('search'); return 'search'; }); const resolved = await Promise.all([p1, p2, p3]); @@ -220,29 +220,29 @@ describe('MCP Protocol Compliance', () => { describe('Protocol Extensions', () => { it('should handle tool-specific extensions', async () => { // Test tool with complex params - const response = await client.callTool('validate_node_operation', { + const response = await client.callTool({ name: 'validate_node_operation', arguments: { nodeType: 'nodes-base.httpRequest', config: { method: 'GET', url: 'https://api.example.com' }, profile: 'runtime' - }); + } }); expect(response).toHaveLength(1); - expect(response[0].type).toBe('text'); + expect((response[0] as any).type).toBe('text'); }); it('should support optional parameters', async () => { // Call with minimal params - const response1 = await client.callTool('list_nodes', {}); + const response1 = await client.callTool({ name: 'list_nodes', arguments: {} }); // Call with all params - const response2 = await client.callTool('list_nodes', { + const response2 = await client.callTool({ name: 'list_nodes', arguments: { limit: 10, category: 'trigger', package: 'n8n-nodes-base' - }); + } }); expect(response1).toBeDefined(); expect(response2).toBeDefined(); @@ -258,7 +258,7 @@ describe('MCP Protocol Compliance', () => { await testClient.connect(clientTransport); // Make a request - const response = await testClient.callTool('get_database_statistics', {}); + const response = await testClient.callTool({ name: 'get_database_statistics', arguments: {} }); expect(response).toBeDefined(); // Close client @@ -266,7 +266,7 @@ describe('MCP Protocol Compliance', () => { // Further requests should fail try { - await testClient.callTool('get_database_statistics', {}); + await testClient.callTool({ name: 'get_database_statistics', arguments: {} }); expect.fail('Should have thrown an error'); } catch (error) { expect(error).toBeDefined(); @@ -284,12 +284,12 @@ describe('MCP Protocol Compliance', () => { await engine.initialize(); const [serverTransport, clientTransport] = InMemoryTransport.createLinkedPair(); - await engine.connect(serverTransport); + await engine.connectToTransport(serverTransport); const testClient = new Client({ name: 'test', version: '1.0.0' }, {}); await testClient.connect(clientTransport); - const response = await testClient.callTool('get_database_statistics', {}); + const response = await testClient.callTool({ name: 'get_database_statistics', arguments: {} }); expect(response).toBeDefined(); await testClient.close(); diff --git a/tests/integration/mcp-protocol/session-management.test.ts b/tests/integration/mcp-protocol/session-management.test.ts index 392766a..8305921 100644 --- a/tests/integration/mcp-protocol/session-management.test.ts +++ b/tests/integration/mcp-protocol/session-management.test.ts @@ -17,7 +17,7 @@ describe('MCP Session Management', () => { describe('Session Lifecycle', () => { it('should establish a new session', async () => { - const { serverTransport, clientTransport } = InMemoryTransport.createLinkedPair(); + const [serverTransport, clientTransport] = InMemoryTransport.createLinkedPair(); await mcpServer.connectToTransport(serverTransport); const client = new Client({ @@ -30,14 +30,14 @@ describe('MCP Session Management', () => { await client.connect(clientTransport); // Session should be established - const serverInfo = await client.getServerInfo(); + const serverInfo = await client.getServerVersion(); expect(serverInfo).toHaveProperty('name', 'n8n-mcp'); await client.close(); }); it('should handle session initialization with capabilities', async () => { - const { serverTransport, clientTransport } = InMemoryTransport.createLinkedPair(); + const [serverTransport, clientTransport] = InMemoryTransport.createLinkedPair(); await mcpServer.connectToTransport(serverTransport); const client = new Client({ @@ -52,14 +52,14 @@ describe('MCP Session Management', () => { await client.connect(clientTransport); - const serverInfo = await client.getServerInfo(); - expect(serverInfo.capabilities).toHaveProperty('tools'); + const serverInfo = await client.getServerVersion(); + expect(serverInfo!.capabilities).toHaveProperty('tools'); await client.close(); }); it('should handle clean session termination', async () => { - const { serverTransport, clientTransport } = InMemoryTransport.createLinkedPair(); + const [serverTransport, clientTransport] = InMemoryTransport.createLinkedPair(); await mcpServer.connectToTransport(serverTransport); const client = new Client({ @@ -70,15 +70,15 @@ describe('MCP Session Management', () => { await client.connect(clientTransport); // Make some requests - await client.callTool('get_database_statistics', {}); - await client.callTool('list_nodes', { limit: 5 }); + await client.callTool({ name: 'get_database_statistics', arguments: {} }); + await client.callTool({ name: 'list_nodes', arguments: { limit: 5 } }); // Clean termination await client.close(); // Client should be closed try { - await client.callTool('get_database_statistics', {}); + await client.callTool({ name: 'get_database_statistics', arguments: {} }); expect.fail('Should not be able to make requests after close'); } catch (error) { expect(error).toBeDefined(); @@ -86,7 +86,7 @@ describe('MCP Session Management', () => { }); it('should handle abrupt disconnection', async () => { - const { serverTransport, clientTransport } = InMemoryTransport.createLinkedPair(); + const [serverTransport, clientTransport] = InMemoryTransport.createLinkedPair(); await mcpServer.connectToTransport(serverTransport); const client = new Client({ @@ -97,14 +97,14 @@ describe('MCP Session Management', () => { await client.connect(clientTransport); // Make a request to ensure connection is active - await client.callTool('get_database_statistics', {}); + await client.callTool({ name: 'get_database_statistics', arguments: {} }); // Simulate abrupt disconnection by closing transport await clientTransport.close(); // Further operations should fail try { - await client.callTool('list_nodes', {}); + await client.callTool({ name: 'list_nodes', arguments: {} }); expect.fail('Should not be able to make requests after transport close'); } catch (error) { expect(error).toBeDefined(); @@ -118,7 +118,7 @@ describe('MCP Session Management', () => { // Create 5 concurrent sessions for (let i = 0; i < 5; i++) { - const { serverTransport, clientTransport } = InMemoryTransport.createLinkedPair(); + const [serverTransport, clientTransport] = InMemoryTransport.createLinkedPair(); await mcpServer.connectToTransport(serverTransport); const client = new Client({ @@ -132,7 +132,7 @@ describe('MCP Session Management', () => { // All sessions should work independently const promises = sessions.map((client, index) => - client.callTool('get_database_statistics', {}) + client.callTool({ name: 'get_database_statistics', arguments: {} }) .then(response => ({ client: index, response })) ); @@ -141,7 +141,7 @@ describe('MCP Session Management', () => { expect(results).toHaveLength(5); results.forEach(result => { expect(result.response).toBeDefined(); - expect(result.response[0].type).toBe('text'); + expect((result.response[0] as any).type).toBe('text'); }); // Clean up all sessions @@ -150,11 +150,11 @@ describe('MCP Session Management', () => { it('should isolate session state', async () => { // Create two sessions - const { serverTransport: st1, clientTransport: ct1 } = InMemoryTransport.createLinkedPair(); - const { serverTransport: st2, clientTransport: ct2 } = InMemoryTransport.createLinkedPair(); + const [st1, ct1] = InMemoryTransport.createLinkedPair(); + const [st2, ct2] = InMemoryTransport.createLinkedPair(); - await mcpEngine.connect(st1); - await mcpEngine.connect(st2); + await mcpServer.connectToTransport(st1); + await mcpServer.connectToTransport(st2); const client1 = new Client({ name: 'client1', version: '1.0.0' }, {}); const client2 = new Client({ name: 'client2', version: '1.0.0' }, {}); @@ -164,12 +164,12 @@ describe('MCP Session Management', () => { // Both should work independently const [response1, response2] = await Promise.all([ - client1.callTool('list_nodes', { limit: 3 }), - client2.callTool('list_nodes', { limit: 5 }) + client1.callTool({ name: 'list_nodes', arguments: { limit: 3 } }), + client2.callTool({ name: 'list_nodes', arguments: { limit: 5 } }) ]); - const nodes1 = JSON.parse(response1[0].text); - const nodes2 = JSON.parse(response2[0].text); + const nodes1 = JSON.parse((response1[0] as any).text); + const nodes2 = JSON.parse((response2[0] as any).text); expect(nodes1).toHaveLength(3); expect(nodes2).toHaveLength(5); @@ -182,25 +182,25 @@ describe('MCP Session Management', () => { describe('Session Recovery', () => { it('should not persist state between sessions', async () => { // First session - const { serverTransport: st1, clientTransport: ct1 } = InMemoryTransport.createLinkedPair(); - await mcpEngine.connect(st1); + const [st1, ct1] = InMemoryTransport.createLinkedPair(); + await mcpServer.connectToTransport(st1); const client1 = new Client({ name: 'client1', version: '1.0.0' }, {}); await client1.connect(ct1); // Make some requests - await client1.callTool('list_nodes', { limit: 10 }); + await client1.callTool({ name: 'list_nodes', arguments: { limit: 10 } }); await client1.close(); // Second session - should be fresh - const { serverTransport: st2, clientTransport: ct2 } = InMemoryTransport.createLinkedPair(); - await mcpEngine.connect(st2); + const [st2, ct2] = InMemoryTransport.createLinkedPair(); + await mcpServer.connectToTransport(st2); const client2 = new Client({ name: 'client2', version: '1.0.0' }, {}); await client2.connect(ct2); // Should work normally - const response = await client2.callTool('get_database_statistics', {}); + const response = await client2.callTool({ name: 'get_database_statistics', arguments: {} }); expect(response).toBeDefined(); await client2.close(); @@ -208,7 +208,7 @@ describe('MCP Session Management', () => { it('should handle rapid session cycling', async () => { for (let i = 0; i < 10; i++) { - const { serverTransport, clientTransport } = InMemoryTransport.createLinkedPair(); + const [serverTransport, clientTransport] = InMemoryTransport.createLinkedPair(); await mcpServer.connectToTransport(serverTransport); const client = new Client({ @@ -219,7 +219,7 @@ describe('MCP Session Management', () => { await client.connect(clientTransport); // Quick operation - const response = await client.callTool('get_database_statistics', {}); + const response = await client.callTool({ name: 'get_database_statistics', arguments: {} }); expect(response).toBeDefined(); await client.close(); @@ -229,7 +229,7 @@ describe('MCP Session Management', () => { describe('Session Metadata', () => { it('should track client information', async () => { - const { serverTransport, clientTransport } = InMemoryTransport.createLinkedPair(); + const [serverTransport, clientTransport] = InMemoryTransport.createLinkedPair(); await mcpServer.connectToTransport(serverTransport); const client = new Client({ @@ -244,7 +244,7 @@ describe('MCP Session Management', () => { await client.connect(clientTransport); // Server should be aware of client - const serverInfo = await client.getServerInfo(); + const serverInfo = await client.getServerVersion(); expect(serverInfo).toBeDefined(); await client.close(); @@ -254,7 +254,7 @@ describe('MCP Session Management', () => { const clients = []; for (const version of ['1.0.0', '1.1.0', '2.0.0']) { - const { serverTransport, clientTransport } = InMemoryTransport.createLinkedPair(); + const [serverTransport, clientTransport] = InMemoryTransport.createLinkedPair(); await mcpServer.connectToTransport(serverTransport); const client = new Client({ @@ -268,11 +268,11 @@ describe('MCP Session Management', () => { // All versions should work const responses = await Promise.all( - clients.map(client => client.getServerInfo()) + clients.map(client => client.getServerVersion()) ); responses.forEach(info => { - expect(info.name).toBe('n8n-mcp'); + expect(info!.name).toBe('n8n-mcp'); }); // Clean up @@ -285,7 +285,7 @@ describe('MCP Session Management', () => { const sessionCount = 50; for (let i = 0; i < sessionCount; i++) { - const { serverTransport, clientTransport } = InMemoryTransport.createLinkedPair(); + const [serverTransport, clientTransport] = InMemoryTransport.createLinkedPair(); await mcpServer.connectToTransport(serverTransport); const client = new Client({ @@ -297,7 +297,7 @@ describe('MCP Session Management', () => { // Light operation if (i % 10 === 0) { - await client.callTool('get_database_statistics', {}); + await client.callTool({ name: 'get_database_statistics', arguments: {} }); } await client.close(); @@ -305,7 +305,7 @@ describe('MCP Session Management', () => { }); it('should handle session with heavy usage', async () => { - const { serverTransport, clientTransport } = InMemoryTransport.createLinkedPair(); + const [serverTransport, clientTransport] = InMemoryTransport.createLinkedPair(); await mcpServer.connectToTransport(serverTransport); const client = new Client({ @@ -322,7 +322,7 @@ describe('MCP Session Management', () => { for (let i = 0; i < requestCount; i++) { const toolName = i % 2 === 0 ? 'list_nodes' : 'get_database_statistics'; const params = toolName === 'list_nodes' ? { limit: 1 } : {}; - promises.push(client.callTool(toolName, params)); + promises.push(client.callTool({ name: toolName as any, arguments: params })); } const responses = await Promise.all(promises); @@ -334,7 +334,7 @@ describe('MCP Session Management', () => { describe('Session Error Recovery', () => { it('should handle errors without breaking session', async () => { - const { serverTransport, clientTransport } = InMemoryTransport.createLinkedPair(); + const [serverTransport, clientTransport] = InMemoryTransport.createLinkedPair(); await mcpServer.connectToTransport(serverTransport); const client = new Client({ @@ -346,23 +346,23 @@ describe('MCP Session Management', () => { // Make an error-inducing request try { - await client.callTool('get_node_info', { + await client.callTool({ name: 'get_node_info', arguments: { nodeType: 'invalid-node-type' - }); + } }); expect.fail('Should have thrown an error'); } catch (error) { expect(error).toBeDefined(); } // Session should still be active - const response = await client.callTool('get_database_statistics', {}); + const response = await client.callTool({ name: 'get_database_statistics', arguments: {} }); expect(response).toBeDefined(); await client.close(); }); it('should handle multiple errors in sequence', async () => { - const { serverTransport, clientTransport } = InMemoryTransport.createLinkedPair(); + const [serverTransport, clientTransport] = InMemoryTransport.createLinkedPair(); await mcpServer.connectToTransport(serverTransport); const client = new Client({ @@ -374,9 +374,9 @@ describe('MCP Session Management', () => { // Multiple error-inducing requests const errorPromises = [ - client.callTool('get_node_info', { nodeType: 'invalid1' }).catch(e => e), - client.callTool('get_node_info', { nodeType: 'invalid2' }).catch(e => e), - client.callTool('get_node_for_task', { task: 'invalid_task' }).catch(e => e) + client.callTool({ name: 'get_node_info', arguments: { nodeType: 'invalid1' } }).catch(e => e), + client.callTool({ name: 'get_node_info', arguments: { nodeType: 'invalid2' } }).catch(e => e), + client.callTool({ name: 'get_node_for_task', arguments: { task: 'invalid_task' } }).catch(e => e) ]; const errors = await Promise.all(errorPromises); @@ -385,7 +385,7 @@ describe('MCP Session Management', () => { }); // Session should still work - const response = await client.callTool('list_nodes', { limit: 1 }); + const response = await client.callTool({ name: 'list_nodes', arguments: { limit: 1 } }); expect(response).toBeDefined(); await client.close(); @@ -395,8 +395,8 @@ describe('MCP Session Management', () => { describe('Session Transport Events', () => { it('should handle transport reconnection', async () => { // Initial connection - const { serverTransport: st1, clientTransport: ct1 } = InMemoryTransport.createLinkedPair(); - await mcpEngine.connect(st1); + const [st1, ct1] = InMemoryTransport.createLinkedPair(); + await mcpServer.connectToTransport(st1); const client = new Client({ name: 'reconnect-client', @@ -406,14 +406,14 @@ describe('MCP Session Management', () => { await client.connect(ct1); // Initial request - const response1 = await client.callTool('get_database_statistics', {}); + const response1 = await client.callTool({ name: 'get_database_statistics', arguments: {} }); expect(response1).toBeDefined(); await client.close(); // New connection with same client - const { serverTransport: st2, clientTransport: ct2 } = InMemoryTransport.createLinkedPair(); - await mcpEngine.connect(st2); + const [st2, ct2] = InMemoryTransport.createLinkedPair(); + await mcpServer.connectToTransport(st2); const newClient = new Client({ name: 'reconnect-client', @@ -423,7 +423,7 @@ describe('MCP Session Management', () => { await newClient.connect(ct2); // Should work normally - const response2 = await newClient.callTool('get_database_statistics', {}); + const response2 = await newClient.callTool({ name: 'get_database_statistics', arguments: {} }); expect(response2).toBeDefined(); await newClient.close(); diff --git a/tests/integration/mcp-protocol/test-helpers.ts b/tests/integration/mcp-protocol/test-helpers.ts index 7807794..d132c46 100644 --- a/tests/integration/mcp-protocol/test-helpers.ts +++ b/tests/integration/mcp-protocol/test-helpers.ts @@ -1,10 +1,11 @@ import { Server } from '@modelcontextprotocol/sdk/server/index.js'; -import { Transport } from '@modelcontextprotocol/sdk'; +import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js'; import { CallToolRequestSchema, ListToolsRequestSchema, InitializeRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; +import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js'; import { N8NDocumentationMCPServer } from '../../../src/mcp/server'; export class TestableN8NMCPServer { @@ -54,27 +55,29 @@ export class TestableN8NMCPServer { // Convert result to content array if needed if (Array.isArray(result) && result.length > 0 && result[0].content) { - return result; + return { + content: result[0].content + }; } return { content: [ { - type: 'text', + type: 'text' as const, text: typeof result === 'string' ? result : JSON.stringify(result, null, 2) } ] }; } catch (error: any) { - return { - content: [ - { - type: 'text', - text: `Error: ${error.message}` - } - ], - isError: true - }; + // If it's already an MCP error, throw it as is + if (error.code && error.message) { + throw error; + } + // Otherwise, wrap it in an MCP error + throw new McpError( + ErrorCode.InternalError, + error.message || 'Unknown error' + ); } }); } diff --git a/tests/integration/mcp-protocol/tool-invocation.test.ts b/tests/integration/mcp-protocol/tool-invocation.test.ts index 456f834..ac8ca03 100644 --- a/tests/integration/mcp-protocol/tool-invocation.test.ts +++ b/tests/integration/mcp-protocol/tool-invocation.test.ts @@ -11,7 +11,7 @@ describe('MCP Tool Invocation', () => { mcpServer = new TestableN8NMCPServer(); await mcpServer.initialize(); - const { serverTransport, clientTransport } = InMemoryTransport.createLinkedPair(); + const [serverTransport, clientTransport] = InMemoryTransport.createLinkedPair(); await mcpServer.connectToTransport(serverTransport); client = new Client({ @@ -32,12 +32,12 @@ describe('MCP Tool Invocation', () => { describe('Node Discovery Tools', () => { describe('list_nodes', () => { it('should list nodes with default parameters', async () => { - const response = await client.callTool('list_nodes', {}); + const response = await client.callTool({ name: 'list_nodes', arguments: {} }); expect(response).toHaveLength(1); - expect(response[0].type).toBe('text'); + expect((response[0] as any).type).toBe('text'); - const nodes = JSON.parse(response[0].text); + const nodes = JSON.parse((response[0] as any).text); expect(Array.isArray(nodes)).toBe(true); expect(nodes.length).toBeGreaterThan(0); @@ -49,11 +49,11 @@ describe('MCP Tool Invocation', () => { }); it('should filter nodes by category', async () => { - const response = await client.callTool('list_nodes', { + const response = await client.callTool({ name: 'list_nodes', arguments: { category: 'trigger' - }); + }}); - const nodes = JSON.parse(response[0].text); + const nodes = JSON.parse((response[0] as any).text); expect(nodes.length).toBeGreaterThan(0); nodes.forEach((node: any) => { expect(node.category).toBe('trigger'); @@ -61,20 +61,20 @@ describe('MCP Tool Invocation', () => { }); it('should limit results', async () => { - const response = await client.callTool('list_nodes', { + const response = await client.callTool({ name: 'list_nodes', arguments: { limit: 5 - }); + }}); - const nodes = JSON.parse(response[0].text); + const nodes = JSON.parse((response[0] as any).text); expect(nodes).toHaveLength(5); }); it('should filter by package', async () => { - const response = await client.callTool('list_nodes', { + const response = await client.callTool({ name: 'list_nodes', arguments: { package: 'n8n-nodes-base' - }); + }}); - const nodes = JSON.parse(response[0].text); + const nodes = JSON.parse((response[0] as any).text); expect(nodes.length).toBeGreaterThan(0); nodes.forEach((node: any) => { expect(node.package).toBe('n8n-nodes-base'); @@ -84,11 +84,11 @@ describe('MCP Tool Invocation', () => { describe('search_nodes', () => { it('should search nodes by keyword', async () => { - const response = await client.callTool('search_nodes', { + const response = await client.callTool({ name: 'search_nodes', arguments: { query: 'webhook' - }); + }}); - const nodes = JSON.parse(response[0].text); + const nodes = JSON.parse((response[0] as any).text); expect(nodes.length).toBeGreaterThan(0); // Should find webhook node @@ -98,49 +98,49 @@ describe('MCP Tool Invocation', () => { it('should support different search modes', async () => { // OR mode - const orResponse = await client.callTool('search_nodes', { + const orResponse = await client.callTool({ name: 'search_nodes', arguments: { query: 'http request', mode: 'OR' - }); - const orNodes = JSON.parse(orResponse[0].text); + }}); + const orNodes = JSON.parse((orResponse[0] as any).text); expect(orNodes.length).toBeGreaterThan(0); // AND mode - const andResponse = await client.callTool('search_nodes', { + const andResponse = await client.callTool({ name: 'search_nodes', arguments: { query: 'http request', mode: 'AND' - }); - const andNodes = JSON.parse(andResponse[0].text); + }}); + const andNodes = JSON.parse((andResponse[0] as any).text); expect(andNodes.length).toBeLessThanOrEqual(orNodes.length); // FUZZY mode - const fuzzyResponse = await client.callTool('search_nodes', { + const fuzzyResponse = await client.callTool({ name: 'search_nodes', arguments: { query: 'htpp requst', // Intentional typos mode: 'FUZZY' - }); - const fuzzyNodes = JSON.parse(fuzzyResponse[0].text); + }}); + const fuzzyNodes = JSON.parse((fuzzyResponse[0] as any).text); expect(fuzzyNodes.length).toBeGreaterThan(0); }); it('should respect result limit', async () => { - const response = await client.callTool('search_nodes', { + const response = await client.callTool({ name: 'search_nodes', arguments: { query: 'node', limit: 3 - }); + }}); - const nodes = JSON.parse(response[0].text); + const nodes = JSON.parse((response[0] as any).text); expect(nodes).toHaveLength(3); }); }); describe('get_node_info', () => { it('should get complete node information', async () => { - const response = await client.callTool('get_node_info', { + const response = await client.callTool({ name: 'get_node_info', arguments: { nodeType: 'nodes-base.httpRequest' - }); + }}); - expect(response[0].type).toBe('text'); - const nodeInfo = JSON.parse(response[0].text); + expect((response[0] as any).type).toBe('text'); + const nodeInfo = JSON.parse((response[0] as any).text); expect(nodeInfo).toHaveProperty('name', 'httpRequest'); expect(nodeInfo).toHaveProperty('displayName'); @@ -150,9 +150,9 @@ describe('MCP Tool Invocation', () => { it('should handle non-existent nodes', async () => { try { - await client.callTool('get_node_info', { + await client.callTool({ name: 'get_node_info', arguments: { nodeType: 'nodes-base.nonExistent' - }); + }}); expect.fail('Should have thrown an error'); } catch (error: any) { expect(error.message).toContain('not found'); @@ -161,9 +161,9 @@ describe('MCP Tool Invocation', () => { it('should handle invalid node type format', async () => { try { - await client.callTool('get_node_info', { + await client.callTool({ name: 'get_node_info', arguments: { nodeType: 'invalidFormat' - }); + }}); expect.fail('Should have thrown an error'); } catch (error: any) { expect(error.message).toContain('not found'); @@ -173,11 +173,11 @@ describe('MCP Tool Invocation', () => { describe('get_node_essentials', () => { it('should return condensed node information', async () => { - const response = await client.callTool('get_node_essentials', { + const response = await client.callTool({ name: 'get_node_essentials', arguments: { nodeType: 'nodes-base.httpRequest' - }); + }}); - const essentials = JSON.parse(response[0].text); + const essentials = JSON.parse((response[0] as any).text); expect(essentials).toHaveProperty('nodeType'); expect(essentials).toHaveProperty('displayName'); @@ -185,11 +185,11 @@ describe('MCP Tool Invocation', () => { expect(essentials).toHaveProperty('examples'); // Should be smaller than full info - const fullResponse = await client.callTool('get_node_info', { + const fullResponse = await client.callTool({ name: 'get_node_info', arguments: { nodeType: 'nodes-base.httpRequest' - }); + }}); - expect(response[0].text.length).toBeLessThan(fullResponse[0].text.length); + expect((response[0] as any).text.length).toBeLessThan((fullResponse[0] as any).text.length); }); }); }); @@ -197,30 +197,30 @@ describe('MCP Tool Invocation', () => { describe('Validation Tools', () => { describe('validate_node_operation', () => { it('should validate valid node configuration', async () => { - const response = await client.callTool('validate_node_operation', { + const response = await client.callTool({ name: 'validate_node_operation', arguments: { nodeType: 'nodes-base.httpRequest', config: { method: 'GET', url: 'https://api.example.com/data' } - }); + }}); - const validation = JSON.parse(response[0].text); + const validation = JSON.parse((response[0] as any).text); expect(validation).toHaveProperty('valid'); expect(validation).toHaveProperty('errors'); expect(validation).toHaveProperty('warnings'); }); it('should detect missing required fields', async () => { - const response = await client.callTool('validate_node_operation', { + const response = await client.callTool({ name: 'validate_node_operation', arguments: { nodeType: 'nodes-base.httpRequest', config: { method: 'GET' // Missing required 'url' field } - }); + }}); - const validation = JSON.parse(response[0].text); + const validation = JSON.parse((response[0] as any).text); expect(validation.valid).toBe(false); expect(validation.errors.length).toBeGreaterThan(0); expect(validation.errors[0].message).toContain('url'); @@ -230,13 +230,13 @@ describe('MCP Tool Invocation', () => { const profiles = ['minimal', 'runtime', 'ai-friendly', 'strict']; for (const profile of profiles) { - const response = await client.callTool('validate_node_operation', { + const response = await client.callTool({ name: 'validate_node_operation', arguments: { nodeType: 'nodes-base.httpRequest', config: { method: 'GET', url: 'https://api.example.com' }, profile - }); + }}); - const validation = JSON.parse(response[0].text); + const validation = JSON.parse((response[0] as any).text); expect(validation).toHaveProperty('profile', profile); } }); @@ -273,11 +273,11 @@ describe('MCP Tool Invocation', () => { } }; - const response = await client.callTool('validate_workflow', { + const response = await client.callTool({ name: 'validate_workflow', arguments: { workflow - }); + }}); - const validation = JSON.parse(response[0].text); + const validation = JSON.parse((response[0] as any).text); expect(validation).toHaveProperty('valid'); expect(validation).toHaveProperty('errors'); expect(validation).toHaveProperty('warnings'); @@ -302,11 +302,11 @@ describe('MCP Tool Invocation', () => { } }; - const response = await client.callTool('validate_workflow', { + const response = await client.callTool({ name: 'validate_workflow', arguments: { workflow - }); + }}); - const validation = JSON.parse(response[0].text); + const validation = JSON.parse((response[0] as any).text); expect(validation.valid).toBe(false); expect(validation.errors.length).toBeGreaterThan(0); }); @@ -347,14 +347,14 @@ describe('MCP Tool Invocation', () => { } }; - const response = await client.callTool('validate_workflow', { + const response = await client.callTool({ name: 'validate_workflow', arguments: { workflow, options: { validateExpressions: true } - }); + }}); - const validation = JSON.parse(response[0].text); + const validation = JSON.parse((response[0] as any).text); expect(validation).toHaveProperty('expressionWarnings'); }); }); @@ -363,36 +363,36 @@ describe('MCP Tool Invocation', () => { describe('Documentation Tools', () => { describe('tools_documentation', () => { it('should get quick start guide', async () => { - const response = await client.callTool('tools_documentation', {}); + const response = await client.callTool({ name: 'tools_documentation', arguments: {} }); - expect(response[0].type).toBe('text'); - expect(response[0].text).toContain('Quick Reference'); + expect((response[0] as any).type).toBe('text'); + expect((response[0] as any).text).toContain('Quick Reference'); }); it('should get specific tool documentation', async () => { - const response = await client.callTool('tools_documentation', { + const response = await client.callTool({ name: 'tools_documentation', arguments: { topic: 'search_nodes' - }); + }}); - expect(response[0].text).toContain('search_nodes'); - expect(response[0].text).toContain('Search nodes by keywords'); + expect((response[0] as any).text).toContain('search_nodes'); + expect((response[0] as any).text).toContain('Search nodes by keywords'); }); it('should get comprehensive documentation', async () => { - const response = await client.callTool('tools_documentation', { + const response = await client.callTool({ name: 'tools_documentation', arguments: { depth: 'full' - }); + }}); - expect(response[0].text.length).toBeGreaterThan(5000); - expect(response[0].text).toContain('Comprehensive'); + expect((response[0] as any).text.length).toBeGreaterThan(5000); + expect((response[0] as any).text).toContain('Comprehensive'); }); it('should handle invalid topics gracefully', async () => { - const response = await client.callTool('tools_documentation', { + const response = await client.callTool({ name: 'tools_documentation', arguments: { topic: 'nonexistent_tool' - }); + }}); - expect(response[0].text).toContain('not found'); + expect((response[0] as any).text).toContain('not found'); }); }); }); @@ -400,9 +400,9 @@ describe('MCP Tool Invocation', () => { describe('AI Tools', () => { describe('list_ai_tools', () => { it('should list AI-capable nodes', async () => { - const response = await client.callTool('list_ai_tools', {}); + const response = await client.callTool({ name: 'list_ai_tools', arguments: {} }); - const aiTools = JSON.parse(response[0].text); + const aiTools = JSON.parse((response[0] as any).text); expect(Array.isArray(aiTools)).toBe(true); expect(aiTools.length).toBeGreaterThan(0); @@ -415,11 +415,11 @@ describe('MCP Tool Invocation', () => { describe('get_node_as_tool_info', () => { it('should provide AI tool usage information', async () => { - const response = await client.callTool('get_node_as_tool_info', { + const response = await client.callTool({ name: 'get_node_as_tool_info', arguments: { nodeType: 'nodes-base.slack' - }); + }}); - const info = JSON.parse(response[0].text); + const info = JSON.parse((response[0] as any).text); expect(info).toHaveProperty('nodeType'); expect(info).toHaveProperty('canBeUsedAsTool'); expect(info).toHaveProperty('requirements'); @@ -431,11 +431,11 @@ describe('MCP Tool Invocation', () => { describe('Task Templates', () => { describe('get_node_for_task', () => { it('should return pre-configured node for task', async () => { - const response = await client.callTool('get_node_for_task', { + const response = await client.callTool({ name: 'get_node_for_task', arguments: { task: 'post_json_request' - }); + }}); - const config = JSON.parse(response[0].text); + const config = JSON.parse((response[0] as any).text); expect(config).toHaveProperty('nodeType'); expect(config).toHaveProperty('displayName'); expect(config).toHaveProperty('parameters'); @@ -444,9 +444,9 @@ describe('MCP Tool Invocation', () => { it('should handle unknown tasks', async () => { try { - await client.callTool('get_node_for_task', { + await client.callTool({ name: 'get_node_for_task', arguments: { task: 'unknown_task' - }); + }}); expect.fail('Should have thrown an error'); } catch (error: any) { expect(error.message).toContain('Unknown task'); @@ -456,9 +456,9 @@ describe('MCP Tool Invocation', () => { describe('list_tasks', () => { it('should list all available tasks', async () => { - const response = await client.callTool('list_tasks', {}); + const response = await client.callTool({ name: 'list_tasks', arguments: {} }); - const tasks = JSON.parse(response[0].text); + const tasks = JSON.parse((response[0] as any).text); expect(Array.isArray(tasks)).toBe(true); expect(tasks.length).toBeGreaterThan(0); @@ -471,11 +471,11 @@ describe('MCP Tool Invocation', () => { }); it('should filter by category', async () => { - const response = await client.callTool('list_tasks', { + const response = await client.callTool({ name: 'list_tasks', arguments: { category: 'HTTP/API' - }); + }}); - const tasks = JSON.parse(response[0].text); + const tasks = JSON.parse((response[0] as any).text); tasks.forEach((task: any) => { expect(task.category).toBe('HTTP/API'); }); @@ -486,18 +486,18 @@ describe('MCP Tool Invocation', () => { describe('Complex Tool Interactions', () => { it('should handle tool chaining', async () => { // Search for nodes - const searchResponse = await client.callTool('search_nodes', { + const searchResponse = await client.callTool({ name: 'search_nodes', arguments: { query: 'slack' - }); - const nodes = JSON.parse(searchResponse[0].text); + }}); + const nodes = JSON.parse((searchResponse[0] as any).text); // Get info for first result const firstNode = nodes[0]; - const infoResponse = await client.callTool('get_node_info', { + const infoResponse = await client.callTool({ name: 'get_node_info', arguments: { nodeType: `${firstNode.package}.${firstNode.name}` - }); + }}); - expect(infoResponse[0].text).toContain(firstNode.name); + expect((infoResponse[0] as any).text).toContain(firstNode.name); }); it('should handle parallel tool calls', async () => { @@ -509,7 +509,7 @@ describe('MCP Tool Invocation', () => { ]; const promises = tools.map(tool => - client.callTool(tool, {}) + client.callTool({ name: tool as any, arguments: {} }) ); const responses = await Promise.all(promises); @@ -517,7 +517,7 @@ describe('MCP Tool Invocation', () => { expect(responses).toHaveLength(tools.length); responses.forEach(response => { expect(response).toHaveLength(1); - expect(response[0].type).toBe('text'); + expect((response[0] as any).type).toBe('text'); }); }); @@ -526,14 +526,14 @@ describe('MCP Tool Invocation', () => { const nodeType = 'nodes-base.httpRequest'; const [fullInfo, essentials, searchResult] = await Promise.all([ - client.callTool('get_node_info', { nodeType }), - client.callTool('get_node_essentials', { nodeType }), - client.callTool('search_nodes', { query: 'httpRequest' }) + client.callTool({ name: 'get_node_info', arguments: { nodeType } }), + client.callTool({ name: 'get_node_essentials', arguments: { nodeType } }), + client.callTool({ name: 'search_nodes', arguments: { query: 'httpRequest' } }) ]); - const full = JSON.parse(fullInfo[0].text); - const essential = JSON.parse(essentials[0].text); - const search = JSON.parse(searchResult[0].text); + const full = JSON.parse((fullInfo[0] as any).text); + const essential = JSON.parse((essentials[0] as any).text); + const search = JSON.parse((searchResult[0] as any).text); // Should all reference the same node expect(full.name).toBe('httpRequest'); @@ -541,4 +541,4 @@ describe('MCP Tool Invocation', () => { expect(search.find((n: any) => n.name === 'httpRequest')).toBeDefined(); }); }); -}); \ No newline at end of file +}); diff --git a/tests/integration/setup/msw-test-server.ts b/tests/integration/setup/msw-test-server.ts index 10dde0d..953fba4 100644 --- a/tests/integration/setup/msw-test-server.ts +++ b/tests/integration/setup/msw-test-server.ts @@ -165,7 +165,7 @@ export const n8nApiMock = { */ mockWorkflowCreate: (response?: any) => { return http.post('*/api/v1/workflows', async ({ request }) => { - const body = await request.json(); + const body = await request.json() as Record; return HttpResponse.json({ data: { id: 'test-workflow-id', diff --git a/tests/mocks/n8n-api/index.ts b/tests/mocks/n8n-api/index.ts index 4dfe9fa..6870c97 100644 --- a/tests/mocks/n8n-api/index.ts +++ b/tests/mocks/n8n-api/index.ts @@ -11,7 +11,7 @@ export * from './data/credentials'; export { http, HttpResponse } from 'msw'; // Export factory utilities -export { n8nHandlerFactory } from '../setup/msw-setup'; +export { n8nHandlerFactory } from '../../setup/msw-setup'; export { n8nApiMock, testDataBuilders, diff --git a/tests/setup/msw-setup.ts b/tests/setup/msw-setup.ts index ba385e1..e33a108 100644 --- a/tests/setup/msw-setup.ts +++ b/tests/setup/msw-setup.ts @@ -113,7 +113,7 @@ export const n8nHandlerFactory = { create: () => http.post('*/api/v1/workflows', async ({ request }) => { - const body = await request.json(); + const body = await request.json() as Record; return HttpResponse.json({ data: { id: 'mock-workflow-id', @@ -126,7 +126,7 @@ export const n8nHandlerFactory = { update: (id: string) => http.patch(`*/api/v1/workflows/${id}`, async ({ request }) => { - const body = await request.json(); + const body = await request.json() as Record; return HttpResponse.json({ data: { id,