Skip to main content
Glama
ooples

MCP Console Automation Server

background-jobs.test.ts.disabled17 kB
/** * Integration Tests for Background Job MCP Handlers * * This test suite validates the MCP server handlers for background job operations, * testing the complete flow from MCP tool calls to JobManager execution and results. */ import { ConsoleAutomationServer } from '../../src/mcp/server.js'; import { JobManager } from '../../src/core/JobManager.js'; import { ConsoleManager } from '../../src/core/ConsoleManager.js'; import { platform } from 'os'; import { spawn } from 'child_process'; // Skip tests on Windows for commands that don't exist or behave differently const isWindows = platform() === 'win32'; describe('Background Jobs Integration Tests', () => { let server: ConsoleAutomationServer; let consoleManager: ConsoleManager; let jobManager: JobManager; beforeAll(async () => { server = new ConsoleAutomationServer(); consoleManager = server['consoleManager']; jobManager = consoleManager.getJobManager(); }); afterAll(async () => { if (server) { await server['destroy'](); } }); beforeEach(async () => { // Clean up any existing jobs before each test const jobs = await jobManager.listJobs(); for (const job of jobs) { if (job.status === 'running') { await jobManager.cancelJob(job.id); } } await jobManager.cleanupJobs({ maxAge: 0 }); }); describe('console_execute_async Handler', () => { test('should execute simple command asynchronously', async () => { const command = isWindows ? 'echo' : 'echo'; const args = ['hello world']; const result = await server['handleExecuteAsync']({ name: 'console_execute_async', arguments: { command, args } }); expect(result.content).toBeDefined(); expect(typeof result.content[0].text).toBe('string'); const response = JSON.parse(result.content[0].text); expect(response.success).toBe(true); expect(response.jobId).toBeDefined(); expect(typeof response.jobId).toBe('string'); }, 10000); test('should handle command with options', async () => { const command = isWindows ? 'dir' : 'ls'; const args = isWindows ? [] : ['-la']; const options = { sessionId: 'test-session-123', timeout: 5000, priority: 5, tags: { test: 'integration' } }; const result = await server['handleExecuteAsync']({ name: 'console_execute_async', arguments: { command, args, options } }); const response = JSON.parse(result.content[0].text); expect(response.success).toBe(true); expect(response.jobId).toBeDefined(); // Verify job was created with correct options const job = await jobManager.getJobStatus(response.jobId); expect(job).toBeDefined(); expect(job!.sessionId).toBe('test-session-123'); expect(job!.timeout).toBe(5000); expect(job!.priority).toBe(5); expect(job!.tags).toEqual({ test: 'integration' }); }, 10000); test('should handle invalid command gracefully', async () => { const command = 'nonexistent-command-xyz'; const args = ['test']; const result = await server['handleExecuteAsync']({ name: 'console_execute_async', arguments: { command, args } }); const response = JSON.parse(result.content[0].text); expect(response.success).toBe(false); expect(response.error).toBeDefined(); expect(response.error).toContain('spawn'); }, 10000); }); describe('console_get_job_status Handler', () => { test('should get status of existing job', async () => { // First create a job const createResult = await server['handleExecuteAsync']({ name: 'console_execute_async', arguments: { command: isWindows ? 'ping' : 'sleep', args: isWindows ? ['-n', '3', 'localhost'] : ['2'] } }); const createResponse = JSON.parse(createResult.content[0].text); const jobId = createResponse.jobId; // Get job status const statusResult = await server['handleGetJobStatus']({ name: 'console_get_job_status', arguments: { jobId } }); const statusResponse = JSON.parse(statusResult.content[0].text); expect(statusResponse.success).toBe(true); expect(statusResponse.job).toBeDefined(); expect(statusResponse.job.id).toBe(jobId); expect(statusResponse.job.command).toBe(isWindows ? 'ping' : 'sleep'); expect(['running', 'completed', 'failed']).toContain(statusResponse.job.status); }, 15000); test('should handle non-existent job', async () => { const result = await server['handleGetJobStatus']({ name: 'console_get_job_status', arguments: { jobId: 'non-existent-id' } }); const response = JSON.parse(result.content[0].text); expect(response.success).toBe(false); expect(response.error).toContain('not found'); }); }); describe('console_get_job_output Handler', () => { test('should get output from completed job', async () => { // Create a simple echo job const createResult = await server['handleExecuteAsync']({ name: 'console_execute_async', arguments: { command: isWindows ? 'echo' : 'echo', args: ['test output message'] } }); const createResponse = JSON.parse(createResult.content[0].text); const jobId = createResponse.jobId; // Wait for job to complete let attempts = 0; let jobCompleted = false; while (attempts < 20 && !jobCompleted) { await new Promise(resolve => setTimeout(resolve, 500)); const job = await jobManager.getJobStatus(jobId); jobCompleted = job && job.status !== 'running'; attempts++; } // Get job output const outputResult = await server['handleGetJobOutput']({ name: 'console_get_job_output', arguments: { jobId } }); const outputResponse = JSON.parse(outputResult.content[0].text); expect(outputResponse.success).toBe(true); expect(outputResponse.output).toBeDefined(); expect(outputResponse.output.stdout).toBeDefined(); expect(outputResponse.output.sequences).toBeDefined(); expect(Array.isArray(outputResponse.output.sequences)).toBe(true); }, 20000); test('should handle output options', async () => { // Create a job that produces both stdout and stderr const createResult = await server['handleExecuteAsync']({ name: 'console_execute_async', arguments: { command: isWindows ? 'cmd' : 'sh', args: isWindows ? ['/c', 'echo stdout & echo stderr 1>&2'] : ['-c', 'echo stdout; echo stderr >&2'] } }); const createResponse = JSON.parse(createResult.content[0].text); const jobId = createResponse.jobId; // Wait for job to complete await new Promise(resolve => setTimeout(resolve, 2000)); // Get output with stderr included const outputResult = await server['handleGetJobOutput']({ name: 'console_get_job_output', arguments: { jobId, options: { includeStderr: true, maxLines: 10 } } }); const outputResponse = JSON.parse(outputResult.content[0].text); expect(outputResponse.success).toBe(true); expect(outputResponse.output.stdout).toBeDefined(); expect(outputResponse.output.stderr).toBeDefined(); }, 15000); }); describe('console_cancel_job Handler', () => { test('should cancel running job', async () => { // Create a long-running job const createResult = await server['handleExecuteAsync']({ name: 'console_execute_async', arguments: { command: isWindows ? 'ping' : 'sleep', args: isWindows ? ['-n', '100', 'localhost'] : ['30'] } }); const createResponse = JSON.parse(createResult.content[0].text); const jobId = createResponse.jobId; // Wait a moment for job to start await new Promise(resolve => setTimeout(resolve, 1000)); // Cancel the job const cancelResult = await server['handleCancelJob']({ name: 'console_cancel_job', arguments: { jobId } }); const cancelResponse = JSON.parse(cancelResult.content[0].text); expect(cancelResponse.success).toBe(true); expect(cancelResponse.cancelled).toBe(true); // Verify job was cancelled const job = await jobManager.getJobStatus(jobId); expect(job!.status).toBe('cancelled'); }, 15000); test('should handle non-existent job cancellation', async () => { const result = await server['handleCancelJob']({ name: 'console_cancel_job', arguments: { jobId: 'non-existent-id' } }); const response = JSON.parse(result.content[0].text); expect(response.success).toBe(true); expect(response.cancelled).toBe(false); }); }); describe('console_list_jobs Handler', () => { test('should list all jobs', async () => { // Create a few jobs const job1 = await server['handleExecuteAsync']({ name: 'console_execute_async', arguments: { command: isWindows ? 'echo' : 'echo', args: ['job1'] } }); const job2 = await server['handleExecuteAsync']({ name: 'console_execute_async', arguments: { command: isWindows ? 'echo' : 'echo', args: ['job2'] } }); // List jobs const listResult = await server['handleListJobs']({ name: 'console_list_jobs', arguments: {} }); const listResponse = JSON.parse(listResult.content[0].text); expect(listResponse.success).toBe(true); expect(listResponse.jobs).toBeDefined(); expect(Array.isArray(listResponse.jobs)).toBe(true); expect(listResponse.jobs.length).toBeGreaterThanOrEqual(2); }, 10000); test('should filter jobs by session', async () => { // Create jobs with different session IDs await server['handleExecuteAsync']({ name: 'console_execute_async', arguments: { command: isWindows ? 'echo' : 'echo', args: ['session1'], options: { sessionId: 'session-1' } } }); await server['handleExecuteAsync']({ name: 'console_execute_async', arguments: { command: isWindows ? 'echo' : 'echo', args: ['session2'], options: { sessionId: 'session-2' } } }); // List jobs for specific session const listResult = await server['handleListJobs']({ name: 'console_list_jobs', arguments: { options: { sessionId: 'session-1' } } }); const listResponse = JSON.parse(listResult.content[0].text); expect(listResponse.success).toBe(true); expect(listResponse.jobs.length).toBeGreaterThanOrEqual(1); expect(listResponse.jobs.every((job: any) => job.sessionId === 'session-1')).toBe(true); }, 10000); test('should respect limit parameter', async () => { // Create multiple jobs for (let i = 0; i < 5; i++) { await server['handleExecuteAsync']({ name: 'console_execute_async', arguments: { command: isWindows ? 'echo' : 'echo', args: [`job${i}`] } }); } // List with limit const listResult = await server['handleListJobs']({ name: 'console_list_jobs', arguments: { options: { limit: 3 } } }); const listResponse = JSON.parse(listResult.content[0].text); expect(listResponse.success).toBe(true); expect(listResponse.jobs.length).toBeLessThanOrEqual(3); }, 15000); }); describe('console_get_job_metrics Handler', () => { test('should return job metrics', async () => { // Create some jobs to have metrics await server['handleExecuteAsync']({ name: 'console_execute_async', arguments: { command: isWindows ? 'echo' : 'echo', args: ['metrics test'] } }); const result = await server['handleGetJobMetrics']({ name: 'console_get_job_metrics', arguments: {} }); const response = JSON.parse(result.content[0].text); expect(response.success).toBe(true); expect(response.metrics).toBeDefined(); expect(typeof response.metrics.totalJobs).toBe('number'); expect(typeof response.metrics.runningJobs).toBe('number'); expect(typeof response.metrics.completedJobs).toBe('number'); expect(typeof response.metrics.failedJobs).toBe('number'); expect(typeof response.metrics.cancelledJobs).toBe('number'); expect(response.metrics.totalJobs).toBeGreaterThanOrEqual(1); }, 10000); }); describe('console_cleanup_jobs Handler', () => { test('should cleanup completed jobs', async () => { // Create and complete a job const createResult = await server['handleExecuteAsync']({ name: 'console_execute_async', arguments: { command: isWindows ? 'echo' : 'echo', args: ['cleanup test'] } }); const createResponse = JSON.parse(createResult.content[0].text); const jobId = createResponse.jobId; // Wait for job to complete await new Promise(resolve => setTimeout(resolve, 2000)); // Cleanup jobs const cleanupResult = await server['handleCleanupJobs']({ name: 'console_cleanup_jobs', arguments: { options: { maxAge: 0 } } }); const cleanupResponse = JSON.parse(cleanupResult.content[0].text); expect(cleanupResponse.success).toBe(true); expect(typeof cleanupResponse.cleaned).toBe('number'); expect(cleanupResponse.cleaned).toBeGreaterThanOrEqual(0); }, 10000); }); describe('Error Handling and Edge Cases', () => { test('should handle malformed request parameters', async () => { try { await server['handleExecuteAsync']({ name: 'console_execute_async', arguments: {} // Missing required command parameter }); } catch (error) { expect(error).toBeDefined(); } }); test('should handle timeout scenarios', async () => { // Create job with short timeout const createResult = await server['handleExecuteAsync']({ name: 'console_execute_async', arguments: { command: isWindows ? 'ping' : 'sleep', args: isWindows ? ['-n', '10', 'localhost'] : ['10'], options: { timeout: 1000 } // 1 second timeout } }); const createResponse = JSON.parse(createResult.content[0].text); const jobId = createResponse.jobId; // Wait for timeout to occur await new Promise(resolve => setTimeout(resolve, 2000)); // Check job status const statusResult = await server['handleGetJobStatus']({ name: 'console_get_job_status', arguments: { jobId } }); const statusResponse = JSON.parse(statusResult.content[0].text); expect(statusResponse.job.status).toBe('failed'); expect(statusResponse.job.error).toContain('timeout'); }, 15000); test('should handle concurrent job operations', async () => { // Start multiple jobs concurrently const promises = []; for (let i = 0; i < 5; i++) { promises.push( server['handleExecuteAsync']({ name: 'console_execute_async', arguments: { command: isWindows ? 'echo' : 'echo', args: [`concurrent-${i}`] } }) ); } const results = await Promise.all(promises); expect(results.length).toBe(5); // Verify all jobs were created successfully results.forEach(result => { const response = JSON.parse(result.content[0].text); expect(response.success).toBe(true); expect(response.jobId).toBeDefined(); }); }, 15000); }); describe('Performance and Stress Testing', () => { test('should handle rapid job creation', async () => { const startTime = Date.now(); const promises = []; // Create 10 jobs rapidly for (let i = 0; i < 10; i++) { promises.push( server['handleExecuteAsync']({ name: 'console_execute_async', arguments: { command: isWindows ? 'echo' : 'echo', args: [`rapid-${i}`] } }) ); } const results = await Promise.all(promises); const endTime = Date.now(); expect(results.length).toBe(10); expect(endTime - startTime).toBeLessThan(5000); // Should complete within 5 seconds // Verify all jobs were created results.forEach(result => { const response = JSON.parse(result.content[0].text); expect(response.success).toBe(true); }); }, 20000); }); });

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/ooples/mcp-console-automation'

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