UNPKG

vibe-coder-mcp

Version:

Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.

89 lines (88 loc) 4.01 kB
import { describe, it, expect, beforeEach, vi } from 'vitest'; import { sseNotifier } from './index.js'; import { JobStatus } from '../job-manager/index.js'; vi.mock('../../logger.js', () => ({ default: { info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn(), } })); const createMockResponse = () => ({ write: vi.fn(), flushHeaders: vi.fn(), on: vi.fn((event, listener) => { if (event === 'close') { mockResponse._closeListener = listener; } return mockResponse; }), off: vi.fn(), writableEnded: false, }); let mockResponse; describe('SseNotifier Singleton', () => { beforeEach(() => { mockResponse = createMockResponse(); vi.clearAllMocks(); sseNotifier.connections.clear(); }); it('should register a new connection', () => { const sessionId = 'session-1'; sseNotifier.registerConnection(sessionId, mockResponse); expect(sseNotifier.connections.has(sessionId)).toBe(true); expect(sseNotifier.connections.get(sessionId)).toBe(mockResponse); expect(mockResponse.write).toHaveBeenCalledWith('event: connection\ndata: established\n\n'); }); it('should unregister a connection', () => { const sessionId = 'session-1'; sseNotifier.registerConnection(sessionId, mockResponse); sseNotifier.unregisterConnection(sessionId); expect(sseNotifier.connections.has(sessionId)).toBe(false); }); it('should automatically unregister when the connection closes', () => { const sessionId = 'session-1'; sseNotifier.registerConnection(sessionId, mockResponse); expect(sseNotifier.connections.has(sessionId)).toBe(true); if (mockResponse._closeListener) { mockResponse._closeListener(); } else { throw new Error("Close listener was not registered by mock"); } expect(sseNotifier.connections.has(sessionId)).toBe(false); }); it('should send progress updates to a registered connection', () => { const sessionId = 'session-1'; const jobId = 'job-123'; const status = JobStatus.RUNNING; const message = 'Processing step 1...'; sseNotifier.registerConnection(sessionId, mockResponse); sseNotifier.sendProgress(sessionId, jobId, status, message); const expectedData = JSON.stringify({ jobId, status, message }); const expectedSseMessage = `event: progress\ndata: ${expectedData}\n\n`; expect(mockResponse.write).toHaveBeenCalledWith(expectedSseMessage); }); it('should not send progress if session ID is not registered', () => { sseNotifier.sendProgress('non-existent-session', 'job-1', JobStatus.RUNNING, 'message'); expect(mockResponse.write).not.toHaveBeenCalledWith(expect.stringContaining('event: progress')); }); it('should handle JSON stringify errors gracefully when sending progress', () => { const sessionId = 'session-1'; sseNotifier.registerConnection(sessionId, mockResponse); const circularData = { jobId: 'job-circ' }; circularData.self = circularData; expect(() => sseNotifier.sendProgress(sessionId, 'job-circ', JobStatus.FAILED, 'Test circular data handling')).not.toThrow(); expect(mockResponse.write).toHaveBeenCalledTimes(1); expect(mockResponse.write).not.toHaveBeenCalledWith(expect.stringContaining('job-circ')); }); it('should not send progress if the connection is already closed (writableEnded)', () => { const sessionId = 'session-1'; sseNotifier.registerConnection(sessionId, mockResponse); mockResponse.writableEnded = true; sseNotifier.sendProgress(sessionId, 'job-1', JobStatus.COMPLETED, 'Done'); expect(mockResponse.write).toHaveBeenCalledTimes(1); expect(mockResponse.write).not.toHaveBeenCalledWith(expect.stringContaining('event: progress')); }); });