Skip to main content
Glama
connector-tools.test.ts107 kB
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { SimplifierClient } from "../../src/client/simplifier-client.js"; import { registerConnectorTools } from "../../src/tools/connector-tools.js"; import { wrapToolResult } from "../../src/tools/toolresult.js"; import { SimplifierConnectorDetails, SimplifierConnectorUpdate, SimplifierConnectorCallUpdate, SimplifierConnectorCallDetails, ConnectorTestResponse } from "../../src/client/types.js"; import { readFile } from "../../src/resourceprovider.js"; // Mock the wrapToolResult function jest.mock("../../src/tools/toolresult.js", () => ({ wrapToolResult: jest.fn() })); // Mock the resourceprovider jest.mock("../../src/resourceprovider.js", () => ({ readFile: jest.fn() })); describe('registerConnectorTools', () => { let mockServer: jest.Mocked<McpServer>; let mockSimplifierClient: jest.Mocked<SimplifierClient>; let mockWrapToolResult: jest.MockedFunction<typeof wrapToolResult>; let mockReadFile: jest.MockedFunction<typeof readFile>; // Helper function to find a tool registration by name const findToolByName = (toolName: string) => { const call = mockServer.registerTool.mock.calls.find(call => call[0] === toolName); if (!call) { throw new Error(`Tool '${toolName}' not found in registered tools`); } return { name: call[0], description: call[1].description, schema: call[1].inputSchema, metadata: call[1].annotations, handler: call[2] }; }; beforeEach(() => { // Create a mock McpServer mockServer = { tool: jest.fn(), registerTool: jest.fn(), } as any; // Create a mock SimplifierClient mockSimplifierClient = { getConnector: jest.fn(), createConnector: jest.fn(), updateConnector: jest.fn(), getConnectorCall: jest.fn(), createConnectorCall: jest.fn(), updateConnectorCall: jest.fn(), testConnectorCall: jest.fn(), deleteConnectorCall: jest.fn(), deleteConnector: jest.fn(), viewRFCFunctions: jest.fn(), rfcWizardGetCallDetails: jest.fn(), rfcWizardCreateCalls: jest.fn() } as any; // Get the mocked functions mockWrapToolResult = wrapToolResult as jest.MockedFunction<typeof wrapToolResult>; mockReadFile = readFile as jest.MockedFunction<typeof readFile>; // Setup default mock for readFile mockReadFile.mockReturnValue("This is the connector documentation content"); // Clear all mocks jest.clearAllMocks(); }); describe('function registration', () => { it('should register all six connector tools', () => { registerConnectorTools(mockServer, mockSimplifierClient); expect(mockServer.registerTool).toHaveBeenCalledTimes(6); // Check the connector-update tool registration const connectorUpdate = findToolByName("connector-update"); expect(connectorUpdate.description).toContain("Create or update a Connector"); expect(connectorUpdate.schema).toMatchObject({ name: expect.any(Object), description: expect.any(Object), tags: expect.any(Object), projectsBefore: expect.any(Object), projectsAfterChange: expect.any(Object), connectorType: expect.any(Object), active: expect.any(Object), endpointConfiguration: expect.any(Object) }); expect(connectorUpdate.metadata).toMatchObject({ title: "Create or update a Connector", readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true }); // Check the connector-call-update tool registration const connectorCallUpdate = findToolByName("connector-call-update"); expect(connectorCallUpdate.description).toContain("Create or update a Connector call"); expect(connectorCallUpdate.schema).toMatchObject({ connectorName: expect.any(Object), connectorCallName: expect.any(Object), description: expect.any(Object), validateIn: expect.any(Object), validateOut: expect.any(Object), async: expect.any(Object), autoGenerated: expect.any(Object), connectorCallParameters: expect.any(Object) }); expect(connectorCallUpdate.metadata).toMatchObject({ title: "Create or update a Connector Call", readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true }); // Check connector-call-test tool registration const connectorCallTest = findToolByName("connector-call-test"); expect(connectorCallTest.schema).toMatchObject({ connectorName: expect.any(Object), callName: expect.any(Object), parameters: expect.any(Object) }); expect(connectorCallTest.metadata).toMatchObject({ title: "Test a Connector Call", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true }); // Check connector-call-delete tool registration const connectorCallDelete = findToolByName("connector-call-delete"); expect(connectorCallDelete.schema).toMatchObject({ connectorName: expect.any(Object), callName: expect.any(Object) }); expect(connectorCallDelete.metadata).toMatchObject({ title: "Delete a Connector Call", readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: true }); // Check connector-delete tool registration const connectorDelete = findToolByName("connector-delete"); expect(connectorDelete.schema).toMatchObject({ connectorName: expect.any(Object) }); expect(connectorDelete.metadata).toMatchObject({ title: "Delete a Connector", readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: true }); }); }); describe('connector-update tool', () => { describe('schema validation', () => { it('should validate required schema fields', () => { registerConnectorTools(mockServer, mockSimplifierClient); const tool = findToolByName("connector-update"); const schema = tool.schema!; // Test that schema validates required fields expect(schema.name).toBeDefined(); expect(schema.description).toBeDefined(); expect(schema.tags).toBeDefined(); expect(schema.projectsBefore).toBeDefined(); expect(schema.projectsAfterChange).toBeDefined(); expect(schema.connectorType).toBeDefined(); expect(schema.active).toBeDefined(); expect(schema.endpointConfiguration).toBeDefined(); // Test valid data passes validation - each field individually const validName = "TestConnector"; const validDescription = "Test connector description"; const validTags = ["test", "connector"]; const validProjectsBefore = ["Project1"]; const validProjectsAfterChange = ["Project1", "Project2"]; const validConnectorType = "REST"; const validActive = true; const validEndpointConfiguration = { endpoint: "https://api.example.com", certificates: [], configuration: {}, loginMethodName: "basicAuth" }; expect(() => schema.name.parse(validName)).not.toThrow(); expect(() => schema.description.parse(validDescription)).not.toThrow(); expect(() => schema.tags.parse(validTags)).not.toThrow(); expect(() => schema.projectsBefore.parse(validProjectsBefore)).not.toThrow(); expect(() => schema.projectsAfterChange.parse(validProjectsAfterChange)).not.toThrow(); expect(() => schema.connectorType.parse(validConnectorType)).not.toThrow(); expect(() => schema.active.parse(validActive)).not.toThrow(); expect(() => schema.endpointConfiguration.parse(validEndpointConfiguration)).not.toThrow(); }); it('should validate that name and connectorType are required', () => { registerConnectorTools(mockServer, mockSimplifierClient); const tool = findToolByName("connector-update"); const schema = tool.schema!; // Test that valid string passes expect(() => schema.name.parse("ValidName")).not.toThrow(); expect(() => schema.connectorType.parse("REST")).not.toThrow(); // Test that undefined fails validation for required fields expect(() => schema.name.parse(undefined)).toThrow(); expect(() => schema.connectorType.parse(undefined)).toThrow(); // Test that null fails validation expect(() => schema.name.parse(null)).toThrow(); expect(() => schema.connectorType.parse(null)).toThrow(); }); it('should allow optional fields to be omitted with defaults', () => { registerConnectorTools(mockServer, mockSimplifierClient); const tool = findToolByName("connector-update"); const schema = tool.schema!; // Test that optional fields can be undefined and get defaults expect(() => schema.description.parse(undefined)).not.toThrow(); expect(() => schema.tags.parse(undefined)).not.toThrow(); expect(() => schema.projectsBefore.parse(undefined)).not.toThrow(); expect(() => schema.projectsAfterChange.parse(undefined)).not.toThrow(); expect(() => schema.active.parse(undefined)).not.toThrow(); // Test default values are applied expect(schema.description.parse(undefined)).toBe(""); expect(schema.tags.parse(undefined)).toEqual([]); expect(schema.projectsBefore.parse(undefined)).toEqual([]); expect(schema.projectsAfterChange.parse(undefined)).toEqual([]); expect(schema.active.parse(undefined)).toBe(true); }); it('should validate endpointConfiguration structure', () => { registerConnectorTools(mockServer, mockSimplifierClient); const tool = findToolByName("connector-update"); const schema = tool.schema!; // Valid endpoint configuration const validConfig = { endpoint: "https://api.example.com", certificates: ["cert1", "cert2"], configuration: { key: "value" }, loginMethodName: "oauth2" }; expect(() => schema.endpointConfiguration.parse(validConfig)).not.toThrow(); // Minimal valid configuration (only required fields) const minimalConfig = { endpoint: "https://api.example.com", certificates: [] }; expect(() => schema.endpointConfiguration.parse(minimalConfig)).not.toThrow(); // Invalid configuration (missing required endpoint) const invalidConfig = { certificates: [] }; expect(() => schema.endpointConfiguration.parse(invalidConfig)).toThrow(); }); }); describe('tool handler - create new connector', () => { let toolHandler: Function; beforeEach(() => { registerConnectorTools(mockServer, mockSimplifierClient); toolHandler = findToolByName("connector-update").handler; }); it('should create a new connector when it does not exist', async () => { const testParams = { name: "NewConnector", description: "New connector description", connectorType: "REST", active: true, endpointConfiguration: { endpoint: "https://api.example.com", certificates: [], configuration: { specificConfig1: "TEST", specificConfig2: "TEST" }, loginMethodName: "basicAuth" }, tags: ["new", "test"], projectsBefore: [], projectsAfterChange: ["Project1"] }; const expectedData: SimplifierConnectorUpdate = { name: "NewConnector", description: "New connector description", connectorType: "REST", active: true, endpointConfiguration: { endpoint: "https://api.example.com", certificates: [], configuration: { specificConfig1: "TEST", specificConfig2: "TEST" }, loginMethodName: "basicAuth" }, tags: ["new", "test"], assignedProjects: { projectsBefore: [], projectsAfterChange: ["Project1"] }, timeoutTime: 0 // Will be added by the actual implementation if needed }; const expectedResponse = "Connector created successfully"; // Mock that connector doesn't exist (throws error) mockSimplifierClient.getConnector.mockRejectedValue( new Error("Not found") ); // Mock successful creation mockSimplifierClient.createConnector.mockResolvedValue(expectedResponse); // Mock wrapToolResult to call the function and return result mockWrapToolResult.mockImplementation(async (_caption, fn) => { const result = await fn(); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; }); await toolHandler(testParams); expect(mockSimplifierClient.getConnector).toHaveBeenCalledWith("NewConnector", "MCP Tool: connector-update"); expect(mockSimplifierClient.createConnector).toHaveBeenCalledWith(expect.objectContaining({ name: expectedData.name, description: expectedData.description, connectorType: expectedData.connectorType, active: expectedData.active, endpointConfiguration: expectedData.endpointConfiguration, tags: expectedData.tags, assignedProjects: expectedData.assignedProjects })); expect(mockSimplifierClient.updateConnector).not.toHaveBeenCalled(); expect(mockWrapToolResult).toHaveBeenCalledWith( "create or update Connector NewConnector", expect.any(Function) ); }); it('should update existing connector when it exists', async () => { const testParams = { name: "ExistingConnector", description: "Updated connector description", connectorType: "SOAP", active: false, endpointConfiguration: { endpoint: "https://api.updated.com", certificates: ["cert1"], configuration: { timeout: 5000 } }, tags: ["updated"], projectsBefore: ["Project1"], projectsAfterChange: ["Project1", "Project2"] }; const existingConnector: SimplifierConnectorDetails = { name: "ExistingConnector", description: "Old description", connectorType: { technicalName: "REST", i18n: "REST Connector", descriptionI18n: "REST API Connector" }, active: true, timeoutTime: 30000, amountOfCalls: 10, editable: true, deletable: true, tags: ["old"], assignedProjects: { projectsBefore: ["Project1"], projectsAfterChange: ["Project1"] }, configuration: { endpoints: [{ endpoint: "https://api.old.com", certificates: [], configuration: {} }] } }; const expectedResponse = "Connector updated successfully"; // Mock that connector exists mockSimplifierClient.getConnector.mockResolvedValue(existingConnector); // Mock successful update mockSimplifierClient.updateConnector.mockResolvedValue(expectedResponse); // Mock wrapToolResult mockWrapToolResult.mockImplementation(async (_caption, fn) => { const result = await fn(); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; }); await toolHandler(testParams); expect(mockSimplifierClient.getConnector).toHaveBeenCalledWith("ExistingConnector", "MCP Tool: connector-update"); expect(mockSimplifierClient.updateConnector).toHaveBeenCalledWith(expect.objectContaining({ name: "ExistingConnector", description: "Updated connector description", connectorType: "SOAP", active: false, endpointConfiguration: testParams.endpointConfiguration, tags: ["updated"], assignedProjects: { projectsBefore: ["Project1"], projectsAfterChange: ["Project1", "Project2"] } })); expect(mockSimplifierClient.createConnector).not.toHaveBeenCalled(); expect(mockWrapToolResult).toHaveBeenCalledWith( "create or update Connector ExistingConnector", expect.any(Function) ); }); it('should handle errors gracefully', async () => { const testParams = { name: "ErrorConnector", description: "Test error handling", connectorType: "REST", active: true, endpointConfiguration: { endpoint: "https://api.error.com", certificates: [] }, tags: [], projectsBefore: [], projectsAfterChange: [] }; const errorMessage = "API Error: Unable to create connector"; // Mock that connector doesn't exist mockSimplifierClient.getConnector.mockRejectedValue(new Error("Not found")); // Mock creation failure mockSimplifierClient.createConnector.mockRejectedValue(new Error(errorMessage)); // Mock wrapToolResult to catch and handle errors mockWrapToolResult.mockImplementation(async (caption, fn) => { try { const result = await fn(); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; } catch (error) { return { content: [{ type: "text", text: JSON.stringify({ error: `Tool ${caption} failed: ${error}` }) }], isError: true }; } }); const result = await toolHandler(testParams); expect(mockSimplifierClient.getConnector).toHaveBeenCalledWith("ErrorConnector", "MCP Tool: connector-update"); expect(mockSimplifierClient.createConnector).toHaveBeenCalledWith(expect.any(Object)); expect(result.isError).toBe(true); expect(result.content[0].text).toContain("failed"); }); it('should handle complex endpoint configurations', async () => { const testParams = { name: "ComplexConnector", description: "Complex configuration test", connectorType: "OAUTH2", active: true, endpointConfiguration: { endpoint: "https://oauth.example.com", certificates: ["cert1", "cert2", "cert3"], configuration: { clientId: "test-client-id", clientSecret: "test-secret", scope: "read write", grantType: "authorization_code", redirectUri: "https://callback.example.com" }, loginMethodName: "oauth2Login" }, tags: ["oauth", "complex", "secure"], projectsBefore: ["ProjectA", "ProjectB"], projectsAfterChange: ["ProjectA", "ProjectC", "ProjectD"] }; // Mock that connector doesn't exist mockSimplifierClient.getConnector.mockRejectedValue(new Error("Not found")); // Mock successful creation mockSimplifierClient.createConnector.mockResolvedValue("Complex connector created"); // Mock wrapToolResult mockWrapToolResult.mockImplementation(async (_caption, fn) => { const result = await fn(); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; }); await toolHandler(testParams); expect(mockSimplifierClient.createConnector).toHaveBeenCalledWith( expect.objectContaining({ endpointConfiguration: testParams.endpointConfiguration }) ); }); it('should create an Oracle SQL connector', async () => { const testParams = { name: "OraExample", description: "connector to oracle database", connectorType: "SQL", active: true, timeoutTime: 60, endpointConfiguration: { endpoint: "Default", loginMethodName: "OracleDBCredentials", certificates: [], configuration: { dataSource: "oracle", host: "172.17.0.3", port: "1521", database: "ORCLCDB", connectionString: "jdbc:oracle:thin:@//172.17.0.3:1521/ORCLCDB", resultType: "resultSet" } }, tags: [], projectsBefore: [], projectsAfterChange: [] }; // Mock that connector doesn't exist mockSimplifierClient.getConnector.mockRejectedValue(new Error("Not found")); // Mock successful creation mockSimplifierClient.createConnector.mockResolvedValue("Oracle SQL connector created"); // Mock wrapToolResult mockWrapToolResult.mockImplementation(async (_caption, fn) => { const result = await fn(); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; }); await toolHandler(testParams); expect(mockSimplifierClient.getConnector).toHaveBeenCalledWith("OraExample", "MCP Tool: connector-update"); expect(mockSimplifierClient.createConnector).toHaveBeenCalledWith( expect.objectContaining({ name: "OraExample", connectorType: "SQL", endpointConfiguration: expect.objectContaining({ endpoint: "Default", loginMethodName: "OracleDBCredentials", configuration: expect.objectContaining({ dataSource: "oracle", host: "172.17.0.3", port: "1521", database: "ORCLCDB", connectionString: "jdbc:oracle:thin:@//172.17.0.3:1521/ORCLCDB", resultType: "resultSet" }) }) }) ); expect(mockSimplifierClient.updateConnector).not.toHaveBeenCalled(); }); it('should create a MySQL SQL connector', async () => { const testParams = { name: "MySQLExample", description: "connector to mysql database", connectorType: "SQL", active: true, timeoutTime: 60, endpointConfiguration: { endpoint: "Default", loginMethodName: "MySQLCredentials", certificates: [], configuration: { dataSource: "mysql", host: "localhost", port: "3306", database: "mydb", connectionString: "jdbc:mysql://localhost:3306/mydb", resultType: "resultSet" } }, tags: ["mysql", "database"], projectsBefore: [], projectsAfterChange: ["DatabaseProject"] }; // Mock that connector doesn't exist mockSimplifierClient.getConnector.mockRejectedValue(new Error("Not found")); // Mock successful creation mockSimplifierClient.createConnector.mockResolvedValue("MySQL SQL connector created"); // Mock wrapToolResult mockWrapToolResult.mockImplementation(async (_caption, fn) => { const result = await fn(); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; }); await toolHandler(testParams); expect(mockSimplifierClient.createConnector).toHaveBeenCalledWith( expect.objectContaining({ name: "MySQLExample", connectorType: "SQL", endpointConfiguration: expect.objectContaining({ configuration: expect.objectContaining({ dataSource: "mysql", connectionString: "jdbc:mysql://localhost:3306/mydb" }) }) }) ); }); it('should create a PostgreSQL SQL connector', async () => { const testParams = { name: "PostgreSQLExample", description: "connector to postgresql database", connectorType: "SQL", active: true, timeoutTime: 60, endpointConfiguration: { endpoint: "Default", loginMethodName: "PostgresCredentials", certificates: [], configuration: { dataSource: "postgresql", host: "db.example.com", port: "5432", database: "proddb", connectionString: "jdbc:postgresql://db.example.com:5432/proddb", resultType: "resultSet" } }, tags: ["postgresql", "database"], projectsBefore: [], projectsAfterChange: [] }; // Mock that connector doesn't exist mockSimplifierClient.getConnector.mockRejectedValue(new Error("Not found")); // Mock successful creation mockSimplifierClient.createConnector.mockResolvedValue("PostgreSQL connector created"); // Mock wrapToolResult mockWrapToolResult.mockImplementation(async (_caption, fn) => { const result = await fn(); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; }); await toolHandler(testParams); expect(mockSimplifierClient.createConnector).toHaveBeenCalledWith( expect.objectContaining({ connectorType: "SQL", endpointConfiguration: expect.objectContaining({ configuration: expect.objectContaining({ dataSource: "postgresql", port: "5432" }) }) }) ); }); it('should create a DB2 SQL connector with schema field', async () => { const testParams = { name: "McpDb2Test", description: "DB2 connector for testdb database", connectorType: "SQL", active: true, timeoutTime: 60, endpointConfiguration: { endpoint: "Default", loginMethodName: "DB2Credentials", certificates: [], configuration: { dataSource: "db2", host: "localhost", port: "50000", database: "testdb", schema: "MYSCHEMA", connectionString: "jdbc:db2://localhost:50000/testdb:currentSchema=MYSCHEMA;", resultType: "resultSet" } }, tags: [], projectsBefore: [], projectsAfterChange: [] }; // Mock that connector doesn't exist mockSimplifierClient.getConnector.mockRejectedValue(new Error("Not found")); // Mock successful creation mockSimplifierClient.createConnector.mockResolvedValue("DB2 SQL connector created"); // Mock wrapToolResult mockWrapToolResult.mockImplementation(async (_caption, fn) => { const result = await fn(); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; }); await toolHandler(testParams); expect(mockSimplifierClient.getConnector).toHaveBeenCalledWith("McpDb2Test", "MCP Tool: connector-update"); expect(mockSimplifierClient.createConnector).toHaveBeenCalledWith( expect.objectContaining({ name: "McpDb2Test", connectorType: "SQL", endpointConfiguration: expect.objectContaining({ endpoint: "Default", loginMethodName: "DB2Credentials", configuration: expect.objectContaining({ dataSource: "db2", host: "localhost", port: "50000", database: "testdb", schema: "MYSCHEMA", connectionString: "jdbc:db2://localhost:50000/testdb:currentSchema=MYSCHEMA;", resultType: "resultSet" }) }) }) ); expect(mockSimplifierClient.updateConnector).not.toHaveBeenCalled(); }); it('should update an existing SQL connector', async () => { const testParams = { name: "ExistingSQLConnector", description: "Updated SQL connector description", connectorType: "SQL", active: true, timeoutTime: 120, endpointConfiguration: { endpoint: "Default", loginMethodName: "UpdatedCredentials", certificates: [], configuration: { dataSource: "oracle", host: "new-host.example.com", port: "1521", database: "NEWDB", connectionString: "jdbc:oracle:thin:@//new-host.example.com:1521/NEWDB", resultType: "resultSet" } }, tags: ["sql", "updated"], projectsBefore: ["Project1"], projectsAfterChange: ["Project1", "Project2"] }; const existingConnector: SimplifierConnectorDetails = { name: "ExistingSQLConnector", description: "Old SQL connector", connectorType: { technicalName: "SQL", i18n: "SQL Connector", descriptionI18n: "SQL Database Connector" }, active: true, timeoutTime: 60, amountOfCalls: 5, editable: true, deletable: true, tags: ["sql"], assignedProjects: { projectsBefore: ["Project1"], projectsAfterChange: ["Project1"] }, configuration: { endpoints: [{ endpoint: "Default", certificates: [], configuration: { dataSource: "oracle", host: "old-host.example.com", port: "1521", database: "OLDDB", connectionString: "jdbc:oracle:thin:@//old-host.example.com:1521/OLDDB", resultType: "resultSet" } }] } }; // Mock that connector exists mockSimplifierClient.getConnector.mockResolvedValue(existingConnector); // Mock successful update mockSimplifierClient.updateConnector.mockResolvedValue("SQL connector updated"); // Mock wrapToolResult mockWrapToolResult.mockImplementation(async (_caption, fn) => { const result = await fn(); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; }); await toolHandler(testParams); expect(mockSimplifierClient.getConnector).toHaveBeenCalledWith("ExistingSQLConnector", "MCP Tool: connector-update"); expect(mockSimplifierClient.updateConnector).toHaveBeenCalledWith( expect.objectContaining({ name: "ExistingSQLConnector", connectorType: "SQL", timeoutTime: 120, endpointConfiguration: expect.objectContaining({ configuration: expect.objectContaining({ host: "new-host.example.com", database: "NEWDB" }) }) }) ); expect(mockSimplifierClient.createConnector).not.toHaveBeenCalled(); }); }); describe('tool handler - correct create/update logic', () => { let toolHandler: Function; beforeEach(() => { registerConnectorTools(mockServer, mockSimplifierClient); toolHandler = findToolByName("connector-update").handler; }); it('should correctly handle create and update operations', async () => { // This test verifies that the create/update logic works correctly: // When connector exists, it should call updateConnector // When connector doesn't exist, it should call createConnector const testParams = { name: "TestConnector", description: "Testing correct logic", connectorType: "REST", active: true, endpointConfiguration: { endpoint: "https://test.com", certificates: [] }, tags: [], projectsBefore: [], projectsAfterChange: [] }; // Case 1: Connector exists - should call updateConnector const existingConnector = { name: "TestConnector", description: "Existing", connectorType: { technicalName: "REST", i18n: "REST", descriptionI18n: "REST" }, active: true, timeoutTime: 30000, amountOfCalls: 0, editable: true, deletable: true, tags: [], assignedProjects: { projectsBefore: [], projectsAfterChange: [] } }; mockSimplifierClient.getConnector.mockResolvedValue(existingConnector); mockSimplifierClient.updateConnector.mockResolvedValue("Updated"); mockWrapToolResult.mockImplementation(async (_caption, fn) => { const result = await fn(); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; }); await toolHandler(testParams); // Should correctly call updateConnector when connector exists expect(mockSimplifierClient.updateConnector).toHaveBeenCalled(); expect(mockSimplifierClient.createConnector).not.toHaveBeenCalled(); // Reset mocks jest.clearAllMocks(); // Case 2: Connector doesn't exist - should call createConnector mockSimplifierClient.getConnector.mockRejectedValue(new Error("Not found")); mockSimplifierClient.createConnector.mockResolvedValue("Created"); await toolHandler(testParams); // Should correctly call createConnector when connector doesn't exist expect(mockSimplifierClient.createConnector).toHaveBeenCalled(); expect(mockSimplifierClient.updateConnector).not.toHaveBeenCalled(); }); }); }); describe('connector-call-update tool', () => { describe('schema validation', () => { it('should validate required schema fields', () => { registerConnectorTools(mockServer, mockSimplifierClient); const tool = findToolByName("connector-call-update"); const schema = tool.schema!; // Test that schema validates required fields expect(schema.connectorName).toBeDefined(); expect(schema.connectorCallName).toBeDefined(); expect(schema.description).toBeDefined(); expect(schema.validateIn).toBeDefined(); expect(schema.validateOut).toBeDefined(); expect(schema.async).toBeDefined(); expect(schema.autoGenerated).toBeDefined(); expect(schema.connectorCallParameters).toBeDefined(); // Test valid data passes validation const validConnectorName = "TestConnector"; const validCallName = "testCall"; const validDescription = "Test call description"; expect(() => schema.connectorName.parse(validConnectorName)).not.toThrow(); expect(() => schema.connectorCallName.parse(validCallName)).not.toThrow(); expect(() => schema.description.parse(validDescription)).not.toThrow(); }); it('should validate that connectorName and connectorCallName are required', () => { registerConnectorTools(mockServer, mockSimplifierClient); const tool = findToolByName("connector-call-update"); const schema = tool.schema!; // Test that valid strings pass expect(() => schema.connectorName.parse("ValidConnector")).not.toThrow(); expect(() => schema.connectorCallName.parse("validCall")).not.toThrow(); // Test that undefined fails validation for required fields expect(() => schema.connectorName.parse(undefined)).toThrow(); expect(() => schema.connectorCallName.parse(undefined)).toThrow(); // Test that null fails validation expect(() => schema.connectorName.parse(null)).toThrow(); expect(() => schema.connectorCallName.parse(null)).toThrow(); }); it('should allow optional fields to be omitted with defaults', () => { registerConnectorTools(mockServer, mockSimplifierClient); const tool = findToolByName("connector-call-update"); const schema = tool.schema!; // Test that optional fields can be undefined and get defaults expect(() => schema.description.parse(undefined)).not.toThrow(); expect(() => schema.validateIn.parse(undefined)).not.toThrow(); expect(() => schema.validateOut.parse(undefined)).not.toThrow(); expect(() => schema.async.parse(undefined)).not.toThrow(); expect(() => schema.autoGenerated.parse(undefined)).not.toThrow(); expect(() => schema.connectorCallParameters.parse(undefined)).not.toThrow(); // Test default values are applied expect(schema.description.parse(undefined)).toBe(""); expect(schema.validateIn.parse(undefined)).toBe(true); expect(schema.validateOut.parse(undefined)).toBe(true); expect(schema.async.parse(undefined)).toBe(false); expect(schema.autoGenerated.parse(undefined)).toBe(false); expect(schema.connectorCallParameters.parse(undefined)).toEqual([]); }); it('should validate connectorCallParameters structure', () => { registerConnectorTools(mockServer, mockSimplifierClient); const tool = findToolByName("connector-call-update"); const schema = tool.schema!; // Valid parameter const validParameter = { name: "inputParam", alias: "input", isInput: true, constValue: "constant", dataType: { name: "String", nameSpace: "base", category: "base" }, optional: false, position: 0 }; expect(() => schema.connectorCallParameters.parse([validParameter])).not.toThrow(); // Minimal valid parameter (only required fields) const minimalParameter = { name: "param1", isInput: true, dataType: { name: "Integer", category: "base" } }; expect(() => schema.connectorCallParameters.parse([minimalParameter])).not.toThrow(); // Invalid parameter (missing required name) const invalidParameter = { isInput: true, dataType: { name: "String", category: "base" } }; expect(() => schema.connectorCallParameters.parse([invalidParameter])).toThrow(); }); it('should validate dataType category enum', () => { registerConnectorTools(mockServer, mockSimplifierClient); const tool = findToolByName("connector-call-update"); const schema = tool.schema!; // Test valid categories const validCategories = ['base', 'domain', 'collection', 'struct']; validCategories.forEach(category => { const parameter = { name: "test", isInput: true, dataType: { name: "TestType", category: category } }; expect(() => schema.connectorCallParameters.parse([parameter])).not.toThrow(); }); // Test invalid category const invalidCategoryParameter = { name: "test", isInput: true, dataType: { name: "TestType", category: "invalid" } }; expect(() => schema.connectorCallParameters.parse([invalidCategoryParameter])).toThrow(); }); it('should validate parameter with special name characters', () => { registerConnectorTools(mockServer, mockSimplifierClient); const tool = findToolByName("connector-call-update"); const schema = tool.schema!; // Test parameter names with special characters (/, []) const specialNameParameters = [ { name: "root/nested/value", isInput: true, dataType: { name: "String", category: "base" } }, { name: "array[]", isInput: false, dataType: { name: "Integer", category: "base" } }, { name: "complex[]/nested/path", isInput: true, dataType: { name: "Object", category: "struct" } } ]; expect(() => schema.connectorCallParameters.parse(specialNameParameters)).not.toThrow(); }); }); describe('tool handler - create new connector call', () => { let toolHandler: Function; beforeEach(() => { registerConnectorTools(mockServer, mockSimplifierClient); toolHandler = findToolByName("connector-call-update").handler; }); it('should create a new connector call when it does not exist', async () => { const testParams = { connectorName: "TestConnector", connectorCallName: "newCall", description: "New call description", validateIn: true, validateOut: true, async: false, autoGenerated: false, connectorCallParameters: [ { name: "inputParam", alias: "input", isInput: true, dataType: { name: "String", nameSpace: "base", category: "base" }, optional: false, position: 0 }, { name: "outputParam", isInput: false, dataType: { name: "Integer", category: "base" }, optional: false, position: 1 } ] }; const expectedData: SimplifierConnectorCallUpdate = { name: "newCall", description: "New call description", validateIn: true, validateOut: true, async: false, autoGenerated: false, connectorCallParameters: testParams.connectorCallParameters }; const expectedResponse = "Connector call created successfully"; // Mock that connector call doesn't exist (throws error) mockSimplifierClient.getConnectorCall.mockRejectedValue( new Error("Not found") ); // Mock successful creation mockSimplifierClient.createConnectorCall.mockResolvedValue(expectedResponse); // Mock wrapToolResult to call the function and return result mockWrapToolResult.mockImplementation(async (_caption, fn) => { const result = await fn(); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; }); await toolHandler(testParams); expect(mockSimplifierClient.getConnectorCall).toHaveBeenCalledWith("TestConnector", "newCall", "MCP Tool: connector-call-update"); expect(mockSimplifierClient.createConnectorCall).toHaveBeenCalledWith( "TestConnector", expect.objectContaining({ name: expectedData.name, description: expectedData.description, validateIn: expectedData.validateIn, validateOut: expectedData.validateOut, async: expectedData.async, autoGenerated: expectedData.autoGenerated, connectorCallParameters: expectedData.connectorCallParameters }) ); expect(mockSimplifierClient.updateConnectorCall).not.toHaveBeenCalled(); expect(mockWrapToolResult).toHaveBeenCalledWith( "create or update Connector call TestConnector.newCall", expect.any(Function) ); }); it('should create a connector call with minimal parameters', async () => { const testParams = { connectorName: "MinimalConnector", connectorCallName: "minimalCall", description: "", validateIn: true, validateOut: true, async: false, autoGenerated: false, connectorCallParameters: [] }; // Mock that connector call doesn't exist mockSimplifierClient.getConnectorCall.mockRejectedValue(new Error("Not found")); // Mock successful creation mockSimplifierClient.createConnectorCall.mockResolvedValue("Created"); // Mock wrapToolResult mockWrapToolResult.mockImplementation(async (_caption, fn) => { const result = await fn(); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; }); await toolHandler(testParams); expect(mockSimplifierClient.createConnectorCall).toHaveBeenCalledWith( "MinimalConnector", expect.objectContaining({ name: "minimalCall", description: "", connectorCallParameters: [] }) ); }); it('should create a connector call with complex parameters', async () => { const testParams = { connectorName: "ComplexConnector", connectorCallName: "complexCall", description: "Complex call with multiple parameter types", validateIn: true, validateOut: false, async: true, autoGenerated: false, connectorCallParameters: [ { name: "root/nested/input", alias: "nestedInput", isInput: true, constValue: "constant-value", dataType: { name: "CustomType", nameSpace: "custom", category: "domain" }, optional: true, position: 0 }, { name: "array[]", isInput: true, dataType: { name: "StringArray", category: "collection" }, optional: false, position: 1 }, { name: "structOutput", isInput: false, dataType: { name: "ComplexStruct", nameSpace: "structures", category: "structure" }, optional: false, position: 2 } ] }; // Mock that connector call doesn't exist mockSimplifierClient.getConnectorCall.mockRejectedValue(new Error("Not found")); // Mock successful creation mockSimplifierClient.createConnectorCall.mockResolvedValue("Complex call created"); // Mock wrapToolResult mockWrapToolResult.mockImplementation(async (_caption, fn) => { const result = await fn(); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; }); await toolHandler(testParams); expect(mockSimplifierClient.createConnectorCall).toHaveBeenCalledWith( "ComplexConnector", expect.objectContaining({ name: "complexCall", async: true, connectorCallParameters: testParams.connectorCallParameters }) ); }); }); describe('tool handler - update existing connector call', () => { let toolHandler: Function; beforeEach(() => { registerConnectorTools(mockServer, mockSimplifierClient); toolHandler = findToolByName("connector-call-update").handler; }); it('should update existing connector call when it exists', async () => { const testParams = { connectorName: "ExistingConnector", connectorCallName: "existingCall", description: "Updated call description", validateIn: false, validateOut: true, async: true, autoGenerated: false, connectorCallParameters: [ { name: "updatedParam", isInput: true, dataType: { name: "Boolean", category: "base" }, optional: true, position: 0 } ] }; const existingCall: SimplifierConnectorCallDetails = { name: "existingCall", description: "Old description", validateIn: true, validateOut: false, async: false, autoGenerated: false, editable: true, executable: true, connectorName: "ExistingConnector", connectorCallParameters: [ { name: "oldParam", isInput: true, dataType: { name: "String", category: "base" }, optional: false, position: 0 } ] }; const expectedResponse = "Connector call updated successfully"; // Mock that connector call exists mockSimplifierClient.getConnectorCall.mockResolvedValue(existingCall); // Mock successful update mockSimplifierClient.updateConnectorCall.mockResolvedValue(expectedResponse); // Mock wrapToolResult mockWrapToolResult.mockImplementation(async (_caption, fn) => { const result = await fn(); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; }); await toolHandler(testParams); expect(mockSimplifierClient.getConnectorCall).toHaveBeenCalledWith("ExistingConnector", "existingCall", "MCP Tool: connector-call-update"); expect(mockSimplifierClient.updateConnectorCall).toHaveBeenCalledWith( "ExistingConnector", expect.objectContaining({ name: "existingCall", description: "Updated call description", validateIn: false, validateOut: true, async: true, autoGenerated: false, connectorCallParameters: testParams.connectorCallParameters }) ); expect(mockSimplifierClient.createConnectorCall).not.toHaveBeenCalled(); expect(mockWrapToolResult).toHaveBeenCalledWith( "create or update Connector call ExistingConnector.existingCall", expect.any(Function) ); }); it('should update connector call with changed parameters', async () => { const testParams = { connectorName: "TestConnector", connectorCallName: "updateCall", description: "Call with updated parameters", validateIn: true, validateOut: true, async: false, autoGenerated: false, connectorCallParameters: [ { name: "newInput1", isInput: true, dataType: { name: "String", category: "base" }, optional: false, position: 0 }, { name: "newInput2", isInput: true, dataType: { name: "Integer", category: "base" }, optional: true, position: 1 }, { name: "newOutput", isInput: false, dataType: { name: "Boolean", category: "base" }, optional: false, position: 2 } ] }; const existingCall: SimplifierConnectorCallDetails = { name: "updateCall", description: "Old description", validateIn: true, validateOut: true, async: false, autoGenerated: false, editable: true, executable: true, connectorName: "TestConnector", connectorCallParameters: [ { name: "oldParam", isInput: true, dataType: { name: "String", category: "base" }, optional: false, position: 0 } ] }; // Mock that connector call exists mockSimplifierClient.getConnectorCall.mockResolvedValue(existingCall); // Mock successful update mockSimplifierClient.updateConnectorCall.mockResolvedValue("Updated"); // Mock wrapToolResult mockWrapToolResult.mockImplementation(async (_caption, fn) => { const result = await fn(); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; }); await toolHandler(testParams); expect(mockSimplifierClient.updateConnectorCall).toHaveBeenCalledWith( "TestConnector", expect.objectContaining({ connectorCallParameters: testParams.connectorCallParameters }) ); }); it('should correctly handle create and update operations', async () => { const testParams = { connectorName: "TestConnector", connectorCallName: "testCall", description: "Testing correct logic", validateIn: true, validateOut: true, async: false, autoGenerated: false, connectorCallParameters: [] }; // Case 1: Call exists - should call updateConnectorCall const existingCall = { name: "testCall", description: "Existing", validateIn: true, validateOut: true, async: false, autoGenerated: false, editable: true, executable: true, connectorName: "TestConnector", connectorCallParameters: [] }; mockSimplifierClient.getConnectorCall.mockResolvedValue(existingCall); mockSimplifierClient.updateConnectorCall.mockResolvedValue("Updated"); mockWrapToolResult.mockImplementation(async (_caption, fn) => { const result = await fn(); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; }); await toolHandler(testParams); // Should correctly call updateConnectorCall when call exists expect(mockSimplifierClient.updateConnectorCall).toHaveBeenCalled(); expect(mockSimplifierClient.createConnectorCall).not.toHaveBeenCalled(); // Reset mocks jest.clearAllMocks(); // Case 2: Call doesn't exist - should call createConnectorCall mockSimplifierClient.getConnectorCall.mockRejectedValue(new Error("Not found")); mockSimplifierClient.createConnectorCall.mockResolvedValue("Created"); mockWrapToolResult.mockImplementation(async (_caption, fn) => { const result = await fn(); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; }); await toolHandler(testParams); // Should correctly call createConnectorCall when call doesn't exist expect(mockSimplifierClient.createConnectorCall).toHaveBeenCalled(); expect(mockSimplifierClient.updateConnectorCall).not.toHaveBeenCalled(); }); }); describe('tool handler - error handling', () => { let toolHandler: Function; beforeEach(() => { registerConnectorTools(mockServer, mockSimplifierClient); toolHandler = findToolByName("connector-call-update").handler; }); it('should handle errors when creating connector call fails', async () => { const testParams = { connectorName: "ErrorConnector", connectorCallName: "errorCall", description: "Test error handling", validateIn: true, validateOut: true, async: false, autoGenerated: false, connectorCallParameters: [] }; const errorMessage = "API Error: Unable to create connector call"; // Mock that connector call doesn't exist mockSimplifierClient.getConnectorCall.mockRejectedValue(new Error("Not found")); // Mock creation failure mockSimplifierClient.createConnectorCall.mockRejectedValue(new Error(errorMessage)); // Mock wrapToolResult to catch and handle errors mockWrapToolResult.mockImplementation(async (caption, fn) => { try { const result = await fn(); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; } catch (error) { return { content: [{ type: "text", text: JSON.stringify({ error: `Tool ${caption} failed: ${error}` }) }], isError: true }; } }); const result = await toolHandler(testParams); expect(mockSimplifierClient.getConnectorCall).toHaveBeenCalledWith("ErrorConnector", "errorCall", "MCP Tool: connector-call-update"); expect(mockSimplifierClient.createConnectorCall).toHaveBeenCalledWith("ErrorConnector", expect.any(Object)); expect(result.isError).toBe(true); expect(result.content[0].text).toContain("failed"); }); it('should handle errors when updating connector call fails', async () => { const testParams = { connectorName: "ErrorConnector", connectorCallName: "errorCall", description: "Test update error", validateIn: true, validateOut: true, async: false, autoGenerated: false, connectorCallParameters: [] }; const existingCall = { name: "errorCall", description: "Existing", validateIn: true, validateOut: true, async: false, autoGenerated: false, editable: true, executable: true, connectorName: "ErrorConnector", connectorCallParameters: [] }; const errorMessage = "API Error: Unable to update connector call"; // Mock that connector call exists mockSimplifierClient.getConnectorCall.mockResolvedValue(existingCall); // Mock update failure mockSimplifierClient.updateConnectorCall.mockRejectedValue(new Error(errorMessage)); // Mock wrapToolResult to catch and handle errors mockWrapToolResult.mockImplementation(async (caption, fn) => { try { const result = await fn(); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; } catch (error) { return { content: [{ type: "text", text: JSON.stringify({ error: `Tool ${caption} failed: ${error}` }) }], isError: true }; } }); const result = await toolHandler(testParams); expect(mockSimplifierClient.getConnectorCall).toHaveBeenCalledWith("ErrorConnector", "errorCall", "MCP Tool: connector-call-update"); expect(mockSimplifierClient.updateConnectorCall).toHaveBeenCalledWith("ErrorConnector", expect.any(Object)); expect(result.isError).toBe(true); expect(result.content[0].text).toContain("failed"); }); it('should handle errors when connector does not exist', async () => { const testParams = { connectorName: "NonExistentConnector", connectorCallName: "testCall", description: "Test on non-existent connector", validateIn: true, validateOut: true, async: false, autoGenerated: false, connectorCallParameters: [] }; // Mock that connector call doesn't exist mockSimplifierClient.getConnectorCall.mockRejectedValue(new Error("Not found")); // Mock that connector doesn't exist mockSimplifierClient.createConnectorCall.mockRejectedValue( new Error("Connector 'NonExistentConnector' does not exist") ); // Mock wrapToolResult to catch and handle errors mockWrapToolResult.mockImplementation(async (caption, fn) => { try { const result = await fn(); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; } catch (error) { return { content: [{ type: "text", text: JSON.stringify({ error: `Tool ${caption} failed: ${error}` }) }], isError: true }; } }); const result = await toolHandler(testParams); expect(result.isError).toBe(true); expect(result.content[0].text).toContain("NonExistentConnector"); }); }); }); describe('connector-wizard-rfc-create tool', () => { describe('schema validation', () => { it('should validate required schema fields', () => { registerConnectorTools(mockServer, mockSimplifierClient); const tool = findToolByName("connector-wizard-rfc-create"); const schema = tool.schema!; // Test that schema validates required fields expect(schema.connectorName).toBeDefined(); expect(schema.rfcFunctionNames).toBeDefined(); // Test valid data passes validation expect(() => schema.connectorName.parse("TestConnector")).not.toThrow(); expect(() => schema.rfcFunctionNames.parse(["FUNCTION1", "FUNCTION2"])).not.toThrow(); }); it('should validate that connectorName and rfcFunctionNames are required', () => { registerConnectorTools(mockServer, mockSimplifierClient); const tool = findToolByName("connector-wizard-rfc-create"); const schema = tool.schema!; // Test that valid values pass expect(() => schema.connectorName.parse("ValidConnector")).not.toThrow(); expect(() => schema.rfcFunctionNames.parse(["FUNC1"])).not.toThrow(); // Test that undefined fails validation expect(() => schema.connectorName.parse(undefined)).toThrow(); expect(() => schema.rfcFunctionNames.parse(undefined)).toThrow(); // Test that null fails validation expect(() => schema.connectorName.parse(null)).toThrow(); expect(() => schema.rfcFunctionNames.parse(null)).toThrow(); }); it('should validate that rfcFunctionNames is an array', () => { registerConnectorTools(mockServer, mockSimplifierClient); const tool = findToolByName("connector-wizard-rfc-create"); const schema = tool.schema!; // Test valid array passes expect(() => schema.rfcFunctionNames.parse([])).not.toThrow(); expect(() => schema.rfcFunctionNames.parse(["FUNC1", "FUNC2", "FUNC3"])).not.toThrow(); // Test non-array fails expect(() => schema.rfcFunctionNames.parse("not an array")).toThrow(); expect(() => schema.rfcFunctionNames.parse({ func: "FUNC1" })).toThrow(); }); it('should validate tool metadata', () => { registerConnectorTools(mockServer, mockSimplifierClient); const tool = findToolByName("connector-wizard-rfc-create"); expect(tool.metadata).toMatchObject({ title: "Create RFC connector calls using the call wizard", readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true }); }); }); describe('tool handler - create RFC calls', () => { let toolHandler: Function; beforeEach(() => { registerConnectorTools(mockServer, mockSimplifierClient); toolHandler = findToolByName("connector-wizard-rfc-create").handler; }); it('should successfully create RFC connector calls', async () => { const testParams = { connectorName: "TestRFCConnector", rfcFunctionNames: ["BAPI_USER_GET_DETAIL", "BAPI_COMPANY_GET_DETAIL"] }; const mockCallDetails = { calls: [ { callId: "call1", call: { name: "BAPI_USER_GET_DETAIL", nameNonTechnicalized: "BAPI_USER_GET_DETAIL", description: "Get user details from SAP" }, meta: {} }, { callId: "call2", call: { name: "BAPI_COMPANY_GET_DETAIL", nameNonTechnicalized: "BAPI_COMPANY_GET_DETAIL", description: "Get company details from SAP" }, meta: {} } ], dataTypes: {} }; const expectedPayload = { callsRfc: ["BAPI_USER_GET_DETAIL", "BAPI_COMPANY_GET_DETAIL"], descriptions: { "BAPI_USER_GET_DETAIL": "Get user details from SAP", "BAPI_COMPANY_GET_DETAIL": "Get company details from SAP" }, newNames: { "BAPI_USER_GET_DETAIL": "BAPI_USER_GET_DETAIL", "BAPI_COMPANY_GET_DETAIL": "BAPI_COMPANY_GET_DETAIL" } }; mockSimplifierClient.viewRFCFunctions.mockResolvedValue(undefined); mockSimplifierClient.rfcWizardGetCallDetails.mockResolvedValue(mockCallDetails); mockSimplifierClient.rfcWizardCreateCalls.mockResolvedValue("Calls created successfully"); mockWrapToolResult.mockImplementation(async (_caption, fn) => { const result = await fn(); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; }); await toolHandler(testParams); expect(mockSimplifierClient.viewRFCFunctions).toHaveBeenCalledWith( "TestRFCConnector", ["BAPI_USER_GET_DETAIL", "BAPI_COMPANY_GET_DETAIL"], "MCP Tool: connector-wizard-rfc-create" ); expect(mockSimplifierClient.rfcWizardGetCallDetails).toHaveBeenCalledWith( "TestRFCConnector", ["BAPI_USER_GET_DETAIL", "BAPI_COMPANY_GET_DETAIL"] ); expect(mockSimplifierClient.rfcWizardCreateCalls).toHaveBeenCalledWith( "TestRFCConnector", expectedPayload ); expect(mockWrapToolResult).toHaveBeenCalledWith( "create 2 connector calls using the RFC connector wizard", expect.any(Function) ); }); it('should handle single RFC function', async () => { const testParams = { connectorName: "TestRFCConnector", rfcFunctionNames: ["SINGLE_FUNCTION"] }; const mockCallDetails = { calls: [ { callId: "call1", call: { name: "SINGLE_FUNCTION", nameNonTechnicalized: "SINGLE_FUNCTION", description: "A single function" }, meta: {} } ], dataTypes: {} }; mockSimplifierClient.viewRFCFunctions.mockResolvedValue(undefined); mockSimplifierClient.rfcWizardGetCallDetails.mockResolvedValue(mockCallDetails); mockSimplifierClient.rfcWizardCreateCalls.mockResolvedValue("Calls created successfully"); mockWrapToolResult.mockImplementation(async (_caption, fn) => { const result = await fn(); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; }); await toolHandler(testParams); expect(mockSimplifierClient.viewRFCFunctions).toHaveBeenCalled(); expect(mockSimplifierClient.rfcWizardGetCallDetails).toHaveBeenCalled(); expect(mockSimplifierClient.rfcWizardCreateCalls).toHaveBeenCalled(); }); it('should handle RFC functions with different names and technical names', async () => { const testParams = { connectorName: "TestRFCConnector", rfcFunctionNames: ["_-ITIZ_-DRAW_READ_ORIGINAL_FILE"] }; const mockCallDetails = { calls: [ { callId: "call1", call: { name: "DRAW_READ_ORIGINAL_FILE", nameNonTechnicalized: "_-ITIZ_-DRAW_READ_ORIGINAL_FILE", description: "Read original file from DRAW" }, meta: {} } ], dataTypes: {} }; const expectedPayload = { callsRfc: ["_-ITIZ_-DRAW_READ_ORIGINAL_FILE"], descriptions: { "_-ITIZ_-DRAW_READ_ORIGINAL_FILE": "Read original file from DRAW" }, newNames: { "_-ITIZ_-DRAW_READ_ORIGINAL_FILE": "DRAW_READ_ORIGINAL_FILE" } }; mockSimplifierClient.viewRFCFunctions.mockResolvedValue(undefined); mockSimplifierClient.rfcWizardGetCallDetails.mockResolvedValue(mockCallDetails); mockSimplifierClient.rfcWizardCreateCalls.mockResolvedValue("Calls created successfully"); mockWrapToolResult.mockImplementation(async (_caption, fn) => { const result = await fn(); expect(result).toEqual(expectedPayload); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; }); await toolHandler(testParams); expect(mockSimplifierClient.rfcWizardCreateCalls).toHaveBeenCalledWith( "TestRFCConnector", expectedPayload ); }); it('should return the created payload', async () => { const testParams = { connectorName: "TestRFCConnector", rfcFunctionNames: ["TEST_FUNC"] }; const mockCallDetails = { calls: [ { callId: "call1", call: { name: "TEST_FUNC", nameNonTechnicalized: "TEST_FUNC", description: "Test function" }, meta: {} } ], dataTypes: {} }; mockSimplifierClient.viewRFCFunctions.mockResolvedValue(undefined); mockSimplifierClient.rfcWizardGetCallDetails.mockResolvedValue(mockCallDetails); mockSimplifierClient.rfcWizardCreateCalls.mockResolvedValue("Calls created successfully"); mockWrapToolResult.mockImplementation(async (_caption, fn) => { const result = await fn(); expect(result).toHaveProperty('callsRfc'); expect(result).toHaveProperty('descriptions'); expect(result).toHaveProperty('newNames'); expect(result.callsRfc).toEqual(["TEST_FUNC"]); expect(result.descriptions["TEST_FUNC"]).toBe("Test function"); expect(result.newNames["TEST_FUNC"]).toBe("TEST_FUNC"); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; }); await toolHandler(testParams); }); }); describe('tool handler - error handling', () => { let toolHandler: Function; beforeEach(() => { registerConnectorTools(mockServer, mockSimplifierClient); toolHandler = findToolByName("connector-wizard-rfc-create").handler; }); it('should handle errors when viewRFCFunctions fails', async () => { const testParams = { connectorName: "TestRFCConnector", rfcFunctionNames: ["TEST_FUNC"] }; mockSimplifierClient.viewRFCFunctions.mockRejectedValue( new Error("Failed to view RFC functions") ); mockWrapToolResult.mockImplementation(async (caption, fn) => { try { await fn(); return { content: [{ type: "text", text: "Success" }] }; } catch (error) { return { content: [{ type: "text", text: JSON.stringify({ error: `Tool ${caption} failed: ${error}` }) }], isError: true }; } }); const result = await toolHandler(testParams); expect(mockSimplifierClient.viewRFCFunctions).toHaveBeenCalled(); expect(result.isError).toBe(true); expect(result.content[0].text).toContain("failed"); }); it('should handle errors when rfcWizardGetCallDetails fails', async () => { const testParams = { connectorName: "TestRFCConnector", rfcFunctionNames: ["TEST_FUNC"] }; mockSimplifierClient.viewRFCFunctions.mockResolvedValue(undefined); mockSimplifierClient.rfcWizardGetCallDetails.mockRejectedValue( new Error("Failed to get call details") ); mockWrapToolResult.mockImplementation(async (caption, fn) => { try { await fn(); return { content: [{ type: "text", text: "Success" }] }; } catch (error) { return { content: [{ type: "text", text: JSON.stringify({ error: `Tool ${caption} failed: ${error}` }) }], isError: true }; } }); const result = await toolHandler(testParams); expect(mockSimplifierClient.viewRFCFunctions).toHaveBeenCalled(); expect(mockSimplifierClient.rfcWizardGetCallDetails).toHaveBeenCalled(); expect(result.isError).toBe(true); }); it('should handle errors when function details cannot be found', async () => { const testParams = { connectorName: "TestRFCConnector", rfcFunctionNames: ["MISSING_FUNC"] }; const mockCallDetails = { calls: [], // Empty - function not found dataTypes: {} }; mockSimplifierClient.viewRFCFunctions.mockResolvedValue(undefined); mockSimplifierClient.rfcWizardGetCallDetails.mockResolvedValue(mockCallDetails); mockWrapToolResult.mockImplementation(async (caption, fn) => { try { await fn(); return { content: [{ type: "text", text: "Success" }] }; } catch (error) { return { content: [{ type: "text", text: JSON.stringify({ error: `Tool ${caption} failed: ${error}` }) }], isError: true }; } }); const result = await toolHandler(testParams); expect(result.isError).toBe(true); expect(result.content[0].text).toContain("MISSING_FUNC"); }); it('should handle errors when rfcWizardCreateCalls fails', async () => { const testParams = { connectorName: "TestRFCConnector", rfcFunctionNames: ["TEST_FUNC"] }; const mockCallDetails = { calls: [ { callId: "call1", call: { name: "TEST_FUNC", nameNonTechnicalized: "TEST_FUNC", description: "Test function" }, meta: {} } ], dataTypes: {} }; mockSimplifierClient.viewRFCFunctions.mockResolvedValue(undefined); mockSimplifierClient.rfcWizardGetCallDetails.mockResolvedValue(mockCallDetails); mockSimplifierClient.rfcWizardCreateCalls.mockRejectedValue( new Error("Failed to create calls") ); mockWrapToolResult.mockImplementation(async (caption, fn) => { try { await fn(); return { content: [{ type: "text", text: "Success" }] }; } catch (error) { return { content: [{ type: "text", text: JSON.stringify({ error: `Tool ${caption} failed: ${error}` }) }], isError: true }; } }); const result = await toolHandler(testParams); expect(mockSimplifierClient.rfcWizardCreateCalls).toHaveBeenCalled(); expect(result.isError).toBe(true); expect(result.content[0].text).toContain("failed"); }); it('should handle connector not found error', async () => { const testParams = { connectorName: "NonExistentConnector", rfcFunctionNames: ["TEST_FUNC"] }; mockSimplifierClient.viewRFCFunctions.mockRejectedValue( new Error("Connector 'NonExistentConnector' not found") ); mockWrapToolResult.mockImplementation(async (caption, fn) => { try { await fn(); return { content: [{ type: "text", text: "Success" }] }; } catch (error) { return { content: [{ type: "text", text: JSON.stringify({ error: `Tool ${caption} failed: ${error}` }) }], isError: true }; } }); const result = await toolHandler(testParams); expect(result.isError).toBe(true); expect(result.content[0].text).toContain("NonExistentConnector"); }); }); }); describe('connector-call-test tool', () => { describe('schema validation', () => { it('should validate required schema fields', () => { registerConnectorTools(mockServer, mockSimplifierClient); const tool = findToolByName("connector-call-test"); const schema = tool.schema!; // Test that schema validates required fields expect(schema.connectorName).toBeDefined(); expect(schema.callName).toBeDefined(); expect(schema.parameters).toBeDefined(); // Test valid data passes validation expect(() => schema.connectorName.parse("TestConnector")).not.toThrow(); expect(() => schema.callName.parse("testCall")).not.toThrow(); expect(() => schema.parameters.parse([{ name: "param1", value: "test" }])).not.toThrow(); }); it('should validate that connectorName and callName are required', () => { registerConnectorTools(mockServer, mockSimplifierClient); const tool = findToolByName("connector-call-test"); const schema = tool.schema!; // Test that valid strings pass expect(() => schema.connectorName.parse("ValidConnector")).not.toThrow(); expect(() => schema.callName.parse("validCall")).not.toThrow(); // Test that undefined fails validation expect(() => schema.connectorName.parse(undefined)).toThrow(); expect(() => schema.callName.parse(undefined)).toThrow(); // Test that null fails validation expect(() => schema.connectorName.parse(null)).toThrow(); expect(() => schema.callName.parse(null)).toThrow(); }); it('should allow parameters to be optional with empty array default', () => { registerConnectorTools(mockServer, mockSimplifierClient); const tool = findToolByName("connector-call-test"); const schema = tool.schema!; // Test that parameters can be undefined and defaults to empty array expect(() => schema.parameters.parse(undefined)).not.toThrow(); expect(schema.parameters.parse(undefined)).toEqual([]); }); it('should validate parameter structure', () => { registerConnectorTools(mockServer, mockSimplifierClient); const tool = findToolByName("connector-call-test"); const schema = tool.schema!; // Test valid parameter structure const validParameters = [ { name: "param1", value: "value1" }, { name: "param2", value: 123 } ]; expect(() => schema.parameters.parse(validParameters)).not.toThrow(); // Test that parameters require a name const invalidParameters = [ { value: "value1" } // Missing name ]; expect(() => schema.parameters.parse(invalidParameters)).toThrow(); }); it('should allow any value type in parameters', () => { registerConnectorTools(mockServer, mockSimplifierClient); const tool = findToolByName("connector-call-test"); const schema = tool.schema!; const parametersWithVariousValues = [ { name: "string", value: "text" }, { name: "number", value: 42 }, { name: "boolean", value: false }, { name: "null", value: null }, { name: "undefined", value: undefined }, { name: "object", value: { nested: "object" } }, { name: "array", value: [1, 2, 3] } ]; expect(() => schema.parameters.parse(parametersWithVariousValues)).not.toThrow(); }); }); describe('tool handler - test connector call', () => { let toolHandler: Function; beforeEach(() => { registerConnectorTools(mockServer, mockSimplifierClient); toolHandler = findToolByName("connector-call-test").handler; }); it('should test connector call successfully with no parameters', async () => { const testParams = { connectorName: "TestConnector", callName: "simpleCall", parameters: [] }; // Mock the connector call details with no parameters const mockConnectorCall: SimplifierConnectorCallDetails = { name: "simpleCall", description: "Simple call", validateIn: true, validateOut: true, async: false, autoGenerated: false, editable: true, executable: true, connectorName: "TestConnector", connectorCallParameters: [] }; mockSimplifierClient.getConnectorCall.mockResolvedValue(mockConnectorCall); const mockResponse: ConnectorTestResponse = { success: true, result: { result: { message: "Success" } } }; mockSimplifierClient.testConnectorCall.mockResolvedValue(mockResponse); mockWrapToolResult.mockImplementation(async (_caption, fn) => { const result = await fn(); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; }); await toolHandler(testParams); expect(mockSimplifierClient.getConnectorCall).toHaveBeenCalledWith("TestConnector", "simpleCall"); expect(mockSimplifierClient.testConnectorCall).toHaveBeenCalledWith( "TestConnector", "simpleCall", { parameters: [] }, "MCP Tool: connector-call-test" ); expect(mockWrapToolResult).toHaveBeenCalledWith( "test connector call TestConnector.simpleCall", expect.any(Function) ); }); it('should test connector call successfully with parameters', async () => { const testParams = { connectorName: "TestConnector", callName: "processData", parameters: [ { name: "inputText", value: "Hello World" }, { name: "count", value: 5 }, { name: "enabled", value: true } ] }; // Mock the connector call details that will be fetched const mockConnectorCall: SimplifierConnectorCallDetails = { name: "processData", description: "Process data", validateIn: true, validateOut: true, async: false, autoGenerated: false, editable: true, executable: true, connectorName: "TestConnector", connectorCallParameters: [ { name: "inputText", isInput: true, dataType: { name: "String", category: "base" }, optional: false, position: 0 }, { name: "count", isInput: true, dataType: { name: "Integer", category: "base" }, optional: false, position: 1 }, { name: "enabled", isInput: true, dataType: { name: "Boolean", category: "base" }, optional: false, position: 2 } ] }; // Mock data types that will be fetched const mockStringType = { id: "string-id", name: "String", category: "base", description: "String type", baseType: "string", isStruct: false, fields: [], properties: [] }; const mockIntegerType = { id: "integer-id", name: "Integer", category: "base", description: "Integer type", baseType: "number", isStruct: false, fields: [], properties: [] }; const mockBooleanType = { id: "boolean-id", name: "Boolean", category: "base", description: "Boolean type", baseType: "boolean", isStruct: false, fields: [], properties: [] }; mockSimplifierClient.getConnectorCall.mockResolvedValue(mockConnectorCall); mockSimplifierClient.getDataTypeByName = jest.fn() .mockResolvedValueOnce(mockStringType) .mockResolvedValueOnce(mockIntegerType) .mockResolvedValueOnce(mockBooleanType); const mockResponse: ConnectorTestResponse = { success: true, result: { result: { processedText: "HELLO WORLD", repeatCount: 5 } } }; mockSimplifierClient.testConnectorCall.mockResolvedValue(mockResponse); mockWrapToolResult.mockImplementation(async (_caption, fn) => { const result = await fn(); expect(result.success).toBe(true); expect(result.result).toEqual({ result: { processedText: "HELLO WORLD", repeatCount: 5 } }); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; }); await toolHandler(testParams); expect(mockSimplifierClient.getConnectorCall).toHaveBeenCalledWith("TestConnector", "processData"); expect(mockSimplifierClient.testConnectorCall).toHaveBeenCalledWith( "TestConnector", "processData", expect.objectContaining({ parameters: expect.arrayContaining([ expect.objectContaining({ name: "inputText", value: "Hello World", dataType: mockStringType, transfer: true }), expect.objectContaining({ name: "count", value: 5, dataType: mockIntegerType, transfer: true }), expect.objectContaining({ name: "enabled", value: true, dataType: mockBooleanType, transfer: true }) ]) }), "MCP Tool: connector-call-test" ); }); it('should handle various parameter value types', async () => { const testParams = { connectorName: "TestConnector", callName: "multiTypeCall", parameters: [ { name: "stringParam", value: "text" }, { name: "numberParam", value: 42 }, { name: "floatParam", value: 3.14 }, { name: "booleanParam", value: true }, { name: "objectParam", value: { key: "value", nested: { data: "test" } } }, { name: "arrayParam", value: [1, 2, 3] }, { name: "nullParam", value: null } ] }; // Mock the connector call details const mockConnectorCall: SimplifierConnectorCallDetails = { name: "multiTypeCall", description: "Multi type call", validateIn: true, validateOut: true, async: false, autoGenerated: false, editable: true, executable: true, connectorName: "TestConnector", connectorCallParameters: testParams.parameters.map((p, i) => ({ name: p.name, isInput: true, dataType: { name: "Any", category: "base" }, optional: false, position: i })) }; const mockDataType = { id: "any-id", name: "Any", category: "base", description: "Any type", baseType: "any", isStruct: false, fields: [], properties: [] }; mockSimplifierClient.getConnectorCall.mockResolvedValue(mockConnectorCall); mockSimplifierClient.getDataTypeByName = jest.fn().mockResolvedValue(mockDataType); const mockResponse: ConnectorTestResponse = { success: true, result: { result: { status: "processed" } } }; mockSimplifierClient.testConnectorCall.mockResolvedValue(mockResponse); mockWrapToolResult.mockImplementation(async (_caption, fn) => { await fn(); return { content: [{ type: "text", text: "Success" }] }; }); await toolHandler(testParams); expect(mockSimplifierClient.getConnectorCall).toHaveBeenCalledWith("TestConnector", "multiTypeCall"); // Check that the call was made with transformed parameters const call = mockSimplifierClient.testConnectorCall.mock.calls[0]; expect(call[0]).toBe("TestConnector"); expect(call[1]).toBe("multiTypeCall"); expect(call[2].parameters).toHaveLength(testParams.parameters.length); // Verify each parameter has the correct structure (excluding null value edge case) testParams.parameters.slice(0, -1).forEach((p, i) => { expect(call[2].parameters[i]).toMatchObject({ name: p.name, value: p.value, dataType: mockDataType, transfer: true }); }); }); it('should handle connector call execution failure', async () => { const testParams = { connectorName: "TestConnector", callName: "failingCall", parameters: [] }; // Mock the connector call details const mockConnectorCall: SimplifierConnectorCallDetails = { name: "failingCall", description: "Failing call", validateIn: true, validateOut: true, async: false, autoGenerated: false, editable: true, executable: true, connectorName: "TestConnector", connectorCallParameters: [] }; mockSimplifierClient.getConnectorCall.mockResolvedValue(mockConnectorCall); const mockResponse: ConnectorTestResponse = { success: false, error: "Connector call failed: Connection timeout" }; mockSimplifierClient.testConnectorCall.mockResolvedValue(mockResponse); mockWrapToolResult.mockImplementation(async (_caption, fn) => { const result = await fn(); expect(result.success).toBe(false); expect(result.error).toBe("Connector call failed: Connection timeout"); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; }); await toolHandler(testParams); expect(mockSimplifierClient.getConnectorCall).toHaveBeenCalledWith("TestConnector", "failingCall"); expect(mockSimplifierClient.testConnectorCall).toHaveBeenCalledWith( "TestConnector", "failingCall", { parameters: [] }, "MCP Tool: connector-call-test" ); }); it('should handle connector call with message field on failure', async () => { const testParams = { connectorName: "TestConnector", callName: "failingCall", parameters: [] }; // Mock the connector call details const mockConnectorCall: SimplifierConnectorCallDetails = { name: "failingCall", description: "Failing call", validateIn: true, validateOut: true, async: false, autoGenerated: false, editable: true, executable: true, connectorName: "TestConnector", connectorCallParameters: [] }; mockSimplifierClient.getConnectorCall.mockResolvedValue(mockConnectorCall); const mockResponse: ConnectorTestResponse = { success: false, message: "Invalid parameters provided" }; mockSimplifierClient.testConnectorCall.mockResolvedValue(mockResponse); mockWrapToolResult.mockImplementation(async (_caption, fn) => { const result = await fn(); expect(result.success).toBe(false); expect(result.error).toBe("Invalid parameters provided"); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; }); await toolHandler(testParams); }); it('should handle connector call with neither error nor message on failure', async () => { const testParams = { connectorName: "TestConnector", callName: "failingCall", parameters: [] }; // Mock the connector call details const mockConnectorCall: SimplifierConnectorCallDetails = { name: "failingCall", description: "Failing call", validateIn: true, validateOut: true, async: false, autoGenerated: false, editable: true, executable: true, connectorName: "TestConnector", connectorCallParameters: [] }; mockSimplifierClient.getConnectorCall.mockResolvedValue(mockConnectorCall); const mockResponse: ConnectorTestResponse = { success: false }; mockSimplifierClient.testConnectorCall.mockResolvedValue(mockResponse); mockWrapToolResult.mockImplementation(async (_caption, fn) => { const result = await fn(); expect(result.success).toBe(false); expect(result.error).toBe("Unknown error"); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; }); await toolHandler(testParams); }); it('should handle client errors (404, 400, 500)', async () => { const testParams = { connectorName: "NonExistentConnector", callName: "nonExistentCall", parameters: [] }; mockSimplifierClient.testConnectorCall.mockRejectedValue( new Error("Connector 'NonExistentConnector' or call 'nonExistentCall' not found") ); mockWrapToolResult.mockImplementation(async (caption, fn) => { try { await fn(); return { content: [{ type: "text", text: "Success" }] }; } catch (error) { return { content: [{ type: "text", text: JSON.stringify({ error: `Tool ${caption} failed: ${error}` }) }] }; } }); await toolHandler(testParams); expect(mockWrapToolResult).toHaveBeenCalledWith( "test connector call NonExistentConnector.nonExistentCall", expect.any(Function) ); }); it('should format successful response correctly', async () => { const testParams = { connectorName: "TestConnector", callName: "successCall", parameters: [{ name: "input", value: "test" }] }; // Mock the connector call details const mockConnectorCall: SimplifierConnectorCallDetails = { name: "successCall", description: "Success call", validateIn: true, validateOut: true, async: false, autoGenerated: false, editable: true, executable: true, connectorName: "TestConnector", connectorCallParameters: [ { name: "input", isInput: true, dataType: { name: "String", category: "base" }, optional: false, position: 0 } ] }; const mockDataType = { id: "string-id", name: "String", category: "base", description: "String type", baseType: "string", isStruct: false, fields: [], properties: [] }; mockSimplifierClient.getConnectorCall.mockResolvedValue(mockConnectorCall); mockSimplifierClient.getDataTypeByName = jest.fn().mockResolvedValue(mockDataType); const mockResponse: ConnectorTestResponse = { success: true, result: { result: { output: "processed test" } } }; mockSimplifierClient.testConnectorCall.mockResolvedValue(mockResponse); mockWrapToolResult.mockImplementation(async (_caption, fn) => { const result = await fn(); expect(result).toEqual({ success: true, message: "Connector call 'successCall' executed successfully", result: {result: {output: "processed test"}} }); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; }); await toolHandler(testParams); }); it('should format failure response correctly', async () => { const testParams = { connectorName: "TestConnector", callName: "failCall", parameters: [] }; // Mock the connector call details const mockConnectorCall: SimplifierConnectorCallDetails = { name: "failCall", description: "Fail call", validateIn: true, validateOut: true, async: false, autoGenerated: false, editable: true, executable: true, connectorName: "TestConnector", connectorCallParameters: [] }; mockSimplifierClient.getConnectorCall.mockResolvedValue(mockConnectorCall); const mockResponse: ConnectorTestResponse = { success: false, error: "Execution error" }; mockSimplifierClient.testConnectorCall.mockResolvedValue(mockResponse); mockWrapToolResult.mockImplementation(async (_caption, fn) => { const result = await fn(); expect(result).toEqual({ success: false, message: "Connector call 'failCall' execution failed", error: "Execution error" }); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; }); await toolHandler(testParams); }); }); }); describe('connector-call-delete tool', () => { let deleteCallToolHandler: Function; beforeEach(() => { registerConnectorTools(mockServer, mockSimplifierClient); deleteCallToolHandler = findToolByName("connector-call-delete").handler; }); describe('delete call tool registration', () => { it('should register delete call tool with correct schema', () => { registerConnectorTools(mockServer, mockSimplifierClient); const tool = findToolByName("connector-call-delete"); const schema = tool.schema!; // Test required fields exist expect(schema.connectorName).toBeDefined(); expect(schema.callName).toBeDefined(); // Test valid data passes validation expect(() => schema.connectorName.parse("TestConnector")).not.toThrow(); expect(() => schema.callName.parse("testCall")).not.toThrow(); // Test that fields are required expect(() => schema.connectorName.parse(undefined)).toThrow(); expect(() => schema.callName.parse(undefined)).toThrow(); expect(() => schema.connectorName.parse(null)).toThrow(); expect(() => schema.callName.parse(null)).toThrow(); }); }); describe('delete call tool handler', () => { it('should successfully delete a connector call', async () => { const testParams = { connectorName: "TestConnector", callName: "testCall" }; const expectedResponse = "Connector call 'testCall' deleted successfully from Connector 'TestConnector'"; mockSimplifierClient.deleteConnectorCall.mockResolvedValue(expectedResponse); mockWrapToolResult.mockImplementation(async (_caption, fn) => { const result = await fn(); return { content: [{type: "text", text: JSON.stringify(result, null, 2)}] }; }); await deleteCallToolHandler(testParams); expect(mockSimplifierClient.deleteConnectorCall).toHaveBeenCalledWith("TestConnector", "testCall", "MCP Tool: connector-call-delete"); expect(mockWrapToolResult).toHaveBeenCalledWith( "delete connector call TestConnector.testCall", expect.any(Function) ); }); it('should handle errors when connector call is not deletable', async () => { const testParams = { connectorName: "TestConnector", callName: "protectedCall" }; mockSimplifierClient.deleteConnectorCall.mockRejectedValue( new Error("Connector call 'protectedCall' cannot be deleted: still in use") ); mockWrapToolResult.mockImplementation(async (caption, fn) => { try { await fn(); return {content: [{type: "text", text: "Success"}]}; } catch (error) { return { content: [{ type: "text", text: JSON.stringify({error: `Tool ${caption} failed: ${error}`}) }] }; } }); await deleteCallToolHandler(testParams); expect(mockSimplifierClient.deleteConnectorCall).toHaveBeenCalledWith("TestConnector", "protectedCall", "MCP Tool: connector-call-delete"); }); it('should return the string response from API on successful deletion', async () => { const testParams = { connectorName: "TestConnector", callName: "testCall" }; const expectedResponse = "Deleted successfully"; mockSimplifierClient.deleteConnectorCall.mockResolvedValue(expectedResponse); mockWrapToolResult.mockImplementation(async (_caption, fn) => { const result = await fn(); expect(result).toBe("Deleted successfully"); return { content: [{type: "text", text: JSON.stringify(result, null, 2)}] }; }); await deleteCallToolHandler(testParams); expect(mockWrapToolResult).toHaveBeenCalled(); }); }); describe('connector-delete tool', () => { let deleteConnectorToolHandler: Function; beforeEach(() => { registerConnectorTools(mockServer, mockSimplifierClient); deleteConnectorToolHandler = findToolByName("connector-delete").handler; }); describe('delete connector tool registration', () => { it('should register delete connector tool with correct schema', () => { registerConnectorTools(mockServer, mockSimplifierClient); const tool = findToolByName("connector-delete"); const schema = tool.schema!; // Test required fields exist expect(schema.connectorName).toBeDefined(); // Test valid data passes validation expect(() => schema.connectorName.parse("TestConnector")).not.toThrow(); // Test that connectorName is required expect(() => schema.connectorName.parse(undefined)).toThrow(); expect(() => schema.connectorName.parse(null)).toThrow(); }); }); describe('delete connector tool handler', () => { it('should successfully delete a connector', async () => { const testParams = { connectorName: "TestConnector" }; const expectedResponse = "Connector 'TestConnector' deleted successfully"; mockSimplifierClient.deleteConnector.mockResolvedValue(expectedResponse); mockWrapToolResult.mockImplementation(async (_caption, fn) => { const result = await fn(); return { content: [{type: "text", text: JSON.stringify(result, null, 2)}] }; }); await deleteConnectorToolHandler(testParams); expect(mockSimplifierClient.deleteConnector).toHaveBeenCalledWith("TestConnector", "MCP Tool: connector-delete"); expect(mockWrapToolResult).toHaveBeenCalledWith( "delete connector TestConnector", expect.any(Function) ); }); it('should handle errors when connector is not deletable', async () => { const testParams = { connectorName: "ProtectedConnector" }; mockSimplifierClient.deleteConnector.mockRejectedValue( new Error("Connector 'ProtectedConnector' cannot be deleted: still in use by Business Objects") ); mockWrapToolResult.mockImplementation(async (caption, fn) => { try { await fn(); return {content: [{type: "text", text: "Success"}]}; } catch (error) { return { content: [{ type: "text", text: JSON.stringify({error: `Tool ${caption} failed: ${error}`}) }] }; } }); await deleteConnectorToolHandler(testParams); expect(mockSimplifierClient.deleteConnector).toHaveBeenCalledWith("ProtectedConnector", "MCP Tool: connector-delete"); }); it('should return the string response from API on successful deletion', async () => { const testParams = { connectorName: "TestConnector" }; const expectedResponse = "Deleted successfully"; mockSimplifierClient.deleteConnector.mockResolvedValue(expectedResponse); mockWrapToolResult.mockImplementation(async (_caption, fn) => { const result = await fn(); expect(result).toBe("Deleted successfully"); return { content: [{type: "text", text: JSON.stringify(result, null, 2)}] }; }); await deleteConnectorToolHandler(testParams); expect(mockWrapToolResult).toHaveBeenCalled(); }); }); }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/SimplifierIO/simplifier-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server