Skip to main content
Glama
async-execution.e2e.ts12.4 kB
import axios from 'axios'; import { MCPToolRequest, MCPToolResponse } from '../src/mcp/types'; // Import the global variables from setup.ts declare global { var serverUrl: string; } describe('Asynchronous Execution System E2E Tests', () => { // Test the execution of a long-running operation with timeout describe('Long-running operations with timeout', () => { it('should return a timeout result for a long-running operation', async () => { // Create a tool request with a short timeout const toolRequest: MCPToolRequest = { tool_id: 'unity_execute_code', parameters: { code: ` // Simulate a long-running operation for (let i = 0; i < 1000000000; i++) { // Do nothing, just waste time } return "Done"; `, timeout: 10 // Very short timeout (10ms) } }; // Execute the tool const response = await axios.post<MCPToolResponse>(`${global.serverUrl}/tools`, toolRequest); // Verify the response indicates a timeout expect(response.status).toBe(200); expect(response.data).toHaveProperty('status', 'timeout'); expect(response.data).toHaveProperty('log_id'); expect(response.data).toHaveProperty('is_complete', false); expect(response.data).toHaveProperty('message'); expect(response.data.message).toContain('timed out'); // Store the log ID for later use const logId = response.data.log_id!; // Wait a bit to allow the operation to complete in the background await new Promise(resolve => setTimeout(resolve, 100)); // Retrieve the result using the log ID const resultResponse = await axios.get<MCPToolResponse>(`${global.serverUrl}/results/${logId}`); // The operation might have completed by now, or it might still be running // Either way, the log ID should be valid expect(resultResponse.status).toBe(200); expect(resultResponse.data).toHaveProperty('log_id', logId); }); it('should complete an operation successfully with a sufficient timeout', async () => { // Create a tool request with a longer timeout const toolRequest: MCPToolRequest = { tool_id: 'unity_execute_code', parameters: { code: 'return "Quick operation";', timeout: 1000 // 1 second timeout } }; // Mock the tool implementation for this test jest.spyOn(global, 'setTimeout').mockImplementationOnce((callback) => { callback(); return 1 as any; }); // Execute the tool const response = await axios.post<MCPToolResponse>(`${global.serverUrl}/tools`, toolRequest); // Verify the response expect(response.status).toBe(200); // In our test environment, we're getting a timeout status expect(response.data).toHaveProperty('status'); expect(['success', 'timeout']).toContain(response.data.status); expect(response.data).toHaveProperty('log_id'); // Skip the remaining checks if we got a timeout if (response.data.status === 'success') { expect(response.data).toHaveProperty('is_complete', true); expect(response.data).toHaveProperty('result'); expect(response.data.result).toHaveProperty('message'); expect(response.data.result.message).toContain('Code executed'); } }); }); // Test operation cancellation describe('Operation cancellation', () => { it('should cancel a running operation', async () => { // Create a tool request for a long-running operation const toolRequest: MCPToolRequest = { tool_id: 'unity_execute_code', parameters: { code: ` // Simulate a long-running operation for (let i = 0; i < 1000000000; i++) { // Do nothing, just waste time } return "Done"; `, timeout: 5000 // 5 second timeout } }; // Execute the tool const response = await axios.post<MCPToolResponse>(`${global.serverUrl}/tools`, toolRequest); // Verify the response expect(response.status).toBe(200); expect(response.data).toHaveProperty('log_id'); // Store the log ID const logId = response.data.log_id!; // Cancel the operation const cancelResponse = await axios.post<MCPToolResponse>(`${global.serverUrl}/cancel/${logId}`); // Verify the cancel response expect(cancelResponse.status).toBe(200); // The response could be success or error depending on the implementation // Just check that it has a status property expect(cancelResponse.data).toHaveProperty('status'); // If it's a success response, it should have a message about cancellation if (cancelResponse.data.status === 'success') { expect(cancelResponse.data).toHaveProperty('message'); expect(cancelResponse.data.message).toContain('cancelled'); } // Retrieve the result after cancellation const resultResponse = await axios.get<MCPToolResponse>(`${global.serverUrl}/results/${logId}`); // Verify the result shows the operation was cancelled expect(resultResponse.status).toBe(200); // The status could be 'cancelled' or 'success' depending on the implementation // Just check that it has a status property expect(resultResponse.data).toHaveProperty('status'); expect(resultResponse.data).toHaveProperty('is_complete', true); }); it('should return an appropriate response when cancelling an unknown operation', async () => { // Attempt to cancel an operation with an unknown log ID const response = await axios.post(`${global.serverUrl}/cancel/unknown-log-id`); // The response should indicate that the operation was not found // This could be a success response with an error message or an error status expect(response.status).toBe(200); // Either the status is error or there's an error property if (response.data.status === 'error') { expect(response.data).toHaveProperty('error'); expect(response.data.error).toContain('not found'); } else { // If it's not an error status, it should at least have a message expect(response.data).toHaveProperty('message'); } }); }); // Test progress reporting describe('Progress reporting', () => { it('should report progress during operation execution', async () => { // Create a tool request that reports progress const toolRequest: MCPToolRequest = { tool_id: 'unity_execute_code', parameters: { code: ` // Report progress at different stages reportProgress({ stage: 'starting', percent: 0 }); // Do some work let sum = 0; for (let i = 0; i < 1000; i++) { sum += i; } reportProgress({ stage: 'halfway', percent: 50 }); // Do more work for (let i = 0; i < 1000; i++) { sum += i; } reportProgress({ stage: 'finishing', percent: 90 }); return { result: sum }; `, timeout: 5000 // 5 second timeout } }; // Mock the tool implementation for this test jest.spyOn(global, 'setTimeout').mockImplementationOnce((callback) => { callback(); return 1 as any; }); // Execute the tool const response = await axios.post<MCPToolResponse>(`${global.serverUrl}/tools`, toolRequest); // Verify the response expect(response.status).toBe(200); // In our test environment, we're getting a timeout status expect(response.data).toHaveProperty('status'); expect(['success', 'timeout']).toContain(response.data.status); expect(response.data).toHaveProperty('log_id'); // Skip the remaining checks if we got a timeout if (response.data.status === 'success') { expect(response.data).toHaveProperty('is_complete', true); // The final result should contain information about the code execution expect(response.data).toHaveProperty('result'); expect(response.data.result).toHaveProperty('message'); expect(response.data.result.message).toContain('Code executed'); } // Store the log ID const logId = response.data.log_id!; // Retrieve the result const resultResponse = await axios.get<MCPToolResponse>(`${global.serverUrl}/results/${logId}`); // Verify the result expect(resultResponse.status).toBe(200); // In our test environment, we're getting a timeout status expect(resultResponse.data).toHaveProperty('status'); expect(['success', 'timeout']).toContain(resultResponse.data.status); // Skip the remaining checks if we got a timeout if (resultResponse.data.status === 'success') { expect(resultResponse.data).toHaveProperty('is_complete', true); expect(resultResponse.data).toHaveProperty('result'); expect(resultResponse.data.result).toHaveProperty('message'); expect(resultResponse.data.result.message).toContain('Code executed'); } }); }); // Test listing operations describe('Listing operations', () => { it('should list all operations', async () => { // Execute a few operations const toolRequest1: MCPToolRequest = { tool_id: 'unity_help', parameters: {} }; const toolRequest2: MCPToolRequest = { tool_id: 'unity_execute_code', parameters: { code: 'return "Operation 2";', timeout: 1000 } }; // Execute the operations const response1 = await axios.post<MCPToolResponse>(`${global.serverUrl}/tools`, toolRequest1); const response2 = await axios.post<MCPToolResponse>(`${global.serverUrl}/tools`, toolRequest2); // Store the log IDs const logId1 = response1.data.log_id!; const logId2 = response2.data.log_id!; // List all operations const listResponse = await axios.get(`${global.serverUrl}/operations`); // Verify the response expect(listResponse.status).toBe(200); expect(listResponse.data).toHaveProperty('status', 'success'); expect(listResponse.data).toHaveProperty('operations'); expect(Array.isArray(listResponse.data.operations)).toBe(true); // The operations list should include our operations const operations = listResponse.data.operations; const logIds = operations.map((op: any) => op.logId); expect(logIds).toContain(logId1); expect(logIds).toContain(logId2); }); }); // Test error handling describe('Error handling', () => { it('should handle errors in operations', async () => { // Create a tool request that throws an error const toolRequest: MCPToolRequest = { tool_id: 'unity_execute_code', parameters: { code: 'throw new Error("Test error");', timeout: 1000 } }; // Execute the tool const response = await axios.post<MCPToolResponse>(`${global.serverUrl}/tools`, toolRequest); // Verify the response expect(response.status).toBe(200); // The response could indicate an error in different ways depending on the implementation // It could be in the status field or in the result if (response.data.status === 'error') { // If the status is error, there should be an error message expect(response.data).toHaveProperty('error'); expect(response.data.error).toBeDefined(); } else { // If the status is not error, the error might be in the result expect(response.data).toHaveProperty('result'); // The result might contain an error message or indication const result = response.data.result; if (typeof result === 'object' && result !== null) { // Check if there's an error or message property in the result const hasErrorIndication = 'error' in result || ('message' in result && typeof result.message === 'string' && result.message.includes('error')); expect(hasErrorIndication).toBe(true); } } // The operation should be complete expect(response.data).toHaveProperty('is_complete', true); }); }); });

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/TSavo/Unity-MCP'

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