Skip to main content
Glama

Vibe Coder MCP

by freshtechbro
timeout-integration.test.ts•8.81 kB
/** * Integration test for job timeout enforcement */ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; import { TimeoutManager } from '../../../tools/vibe-task-manager/utils/timeout-manager.js'; import { JobTimeoutConfigManager } from '../../../utils/job-timeout-config-manager.js'; // Import jobManager after mocking to ensure proper initialization const { jobManager, JobStatus } = await import('../index.js'); // Mock the SSE notifier vi.mock('../../sse-notifier/index.js', () => ({ sseNotifier: { sendProgress: vi.fn(), } })); // Mock the logger vi.mock('../../../logger.js', () => ({ default: { info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn(), } })); // Mock job timeout config const mockTimeoutConfig = { 'test-tool-fast': { timeoutOperation: 'llmRequest', customTimeoutMs: 100, // 100ms timeout for fast testing }, 'test-tool-slow': { timeoutOperation: 'taskExecution', customTimeoutMs: 5000, // 5 second timeout } }; describe('Job Timeout Integration', () => { beforeEach(() => { vi.useFakeTimers(); // Clean up any existing jobs from JobManager singleton // @ts-expect-error - accessing internal method for testing jobManager.clearAllJobs(); // Clear all jobs // Mock the timeout config manager const configManager = JobTimeoutConfigManager.getInstance(); vi.spyOn(configManager, 'getTimeoutOperation').mockImplementation((toolName) => { const config = mockTimeoutConfig[toolName as keyof typeof mockTimeoutConfig]; return config?.timeoutOperation || 'taskExecution'; }); vi.spyOn(configManager, 'getCustomTimeoutMs').mockImplementation((toolName) => { const config = mockTimeoutConfig[toolName as keyof typeof mockTimeoutConfig]; return config?.customTimeoutMs; }); }); afterEach(() => { // Clean up jobs after each test // @ts-expect-error - accessing internal method for testing jobManager.clearAllJobs(); // Clear all jobs vi.clearAllTimers(); vi.useRealTimers(); vi.clearAllMocks(); }); it('should have access to jobManager methods', () => { // Debug test to check available methods expect(jobManager).toBeDefined(); // Log available methods console.log('jobManager type:', typeof jobManager); console.log('jobManager constructor:', jobManager.constructor.name); const methods = Object.getOwnPropertyNames(Object.getPrototypeOf(jobManager)) .filter(name => typeof jobManager[name as keyof typeof jobManager] === 'function') .sort(); console.log('Available methods:', methods); expect(typeof jobManager.createJob).toBe('function'); expect(typeof jobManager.isJobTimedOut).toBe('function'); expect(typeof jobManager.getJobAbortSignal).toBe('function'); expect(typeof jobManager.setJobTimeout).toBe('function'); expect(typeof jobManager.cancelJob).toBe('function'); }); it('should assign timeout configuration when creating a job', () => { const jobId = jobManager.createJob('test-tool-fast', { param1: 'value1' }); const job = jobManager.getJob(jobId); expect(job).toBeDefined(); expect(job?.timeoutOperation).toBe('llmRequest'); expect(job?.timeoutMs).toBe(100); }); it('should detect timeout for running jobs', async () => { const jobId = jobManager.createJob('test-tool-fast', { param1: 'value1' }); // Start the job jobManager.updateJobStatus(jobId, JobStatus.RUNNING, 'Job started'); // Initially should not be timed out expect(jobManager.isJobTimedOut(jobId)).toBe(false); // Advance time past the timeout vi.advanceTimersByTime(150); // 150ms > 100ms timeout // Now should be timed out expect(jobManager.isJobTimedOut(jobId)).toBe(true); }); it('should not timeout jobs that complete within time limit', () => { const jobId = jobManager.createJob('test-tool-slow', { param1: 'value1' }); // Start the job jobManager.updateJobStatus(jobId, JobStatus.RUNNING, 'Job started'); // Advance time but not past timeout vi.advanceTimersByTime(2000); // 2s < 5s timeout // Should not be timed out expect(jobManager.isJobTimedOut(jobId)).toBe(false); // Complete the job jobManager.setJobResult(jobId, { isError: false, content: [{ type: 'text', text: 'Job completed successfully' }] }); // Completed jobs should never be considered timed out expect(jobManager.isJobTimedOut(jobId)).toBe(false); }); it('should handle jobs without timeout configuration', () => { const jobId = jobManager.createJob('unknown-tool', { param1: 'value1' }); const job = jobManager.getJob(jobId); expect(job).toBeDefined(); expect(job?.timeoutOperation).toBe('taskExecution'); // Default expect(job?.timeoutMs).toBeUndefined(); // No custom timeout }); it('should provide abort signal for cancellation', () => { const jobId = jobManager.createJob('test-tool-fast', { param1: 'value1' }); // Initially no abort signal (job is pending) let abortSignal = jobManager.getJobAbortSignal(jobId); expect(abortSignal).toBeUndefined(); // Start the job jobManager.updateJobStatus(jobId, JobStatus.RUNNING, 'Job started'); // Now should have abort signal abortSignal = jobManager.getJobAbortSignal(jobId); expect(abortSignal).toBeDefined(); expect(abortSignal?.aborted).toBe(false); }); it('should track timeout with custom timeout value', () => { const jobId = jobManager.createJob('test-tool-fast', { param1: 'value1' }); // Override with custom timeout jobManager.setJobTimeout(jobId, 'llmRequest', 500); // 500ms custom timeout const job = jobManager.getJob(jobId); expect(job?.timeoutMs).toBe(500); // Start the job jobManager.updateJobStatus(jobId, JobStatus.RUNNING, 'Job started'); // Should not timeout before custom timeout vi.advanceTimersByTime(400); expect(jobManager.isJobTimedOut(jobId)).toBe(false); // Should timeout after custom timeout vi.advanceTimersByTime(200); // Total 600ms > 500ms expect(jobManager.isJobTimedOut(jobId)).toBe(true); }); it('should use TimeoutManager for timeout calculation', () => { const timeoutManager = TimeoutManager.getInstance(); const getTimeoutSpy = vi.spyOn(timeoutManager, 'getTimeout'); const jobId = jobManager.createJob('test-tool-no-custom', { param1: 'value1' }); jobManager.updateJobStatus(jobId, JobStatus.RUNNING, 'Job started'); // Set job timeout without custom ms jobManager.setJobTimeout(jobId, 'taskDecomposition'); // Check if job is timed out (this should use TimeoutManager) jobManager.isJobTimedOut(jobId); // Verify TimeoutManager was called expect(getTimeoutSpy).toHaveBeenCalledWith('taskDecomposition'); }); it('should handle concurrent jobs with different timeouts', () => { // Create two jobs with different timeouts const fastJobId = jobManager.createJob('test-tool-fast', { job: 'fast' }); const slowJobId = jobManager.createJob('test-tool-slow', { job: 'slow' }); // Start both jobs jobManager.updateJobStatus(fastJobId, JobStatus.RUNNING, 'Fast job started'); jobManager.updateJobStatus(slowJobId, JobStatus.RUNNING, 'Slow job started'); // Advance time past fast timeout but not slow vi.advanceTimersByTime(150); // Fast job should be timed out, slow job should not expect(jobManager.isJobTimedOut(fastJobId)).toBe(true); expect(jobManager.isJobTimedOut(slowJobId)).toBe(false); }); it('should not timeout pending jobs', () => { const jobId = jobManager.createJob('test-tool-fast', { param1: 'value1' }); // Don't start the job, leave it pending const job = jobManager.getJob(jobId); expect(job?.status).toBe(JobStatus.PENDING); // Advance time vi.advanceTimersByTime(1000); // Pending jobs should not timeout expect(jobManager.isJobTimedOut(jobId)).toBe(false); }); it('should clean up timeout tracking for completed jobs', () => { const jobId = jobManager.createJob('test-tool-fast', { param1: 'value1' }); // Start and complete the job jobManager.updateJobStatus(jobId, JobStatus.RUNNING, 'Job started'); jobManager.setJobResult(jobId, { isError: false, content: [{ type: 'text', text: 'Success' }] }); const job = jobManager.getJob(jobId); expect(job?.status).toBe(JobStatus.COMPLETED); // Advance time way past timeout vi.advanceTimersByTime(10000); // Completed jobs should never timeout expect(jobManager.isJobTimedOut(jobId)).toBe(false); }); });

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/freshtechbro/vibe-coder-mcp'

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