Path: blob/main/extensions/copilot/src/extension/chatSessions/copilotcli/vscode-node/test/inProcHttpServer.spec.ts
13406 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { beforeEach, describe, expect, it, vi } from 'vitest';6import { TestLogService } from '../../../../../platform/testing/common/testLogService';7import { CopilotCLISessionTracker } from '../copilotCLISessionTracker';8import { InProcHttpServer } from '../inProcHttpServer';910vi.mock('vscode', () => ({11Uri: {12from: (components: { scheme: string; path: string; fragment: string }) => ({13scheme: components.scheme,14path: components.path,15fragment: components.fragment,16}),17},18window: {19onDidCloseTerminal: () => ({ dispose: () => { } }),20},21EventEmitter: class MockEventEmitter<T> {22private readonly listeners: Array<(e: T) => void> = [];23readonly event = (listener: (e: T) => void) => {24this.listeners.push(listener);25return {26dispose: () => {27const idx = this.listeners.indexOf(listener);28if (idx >= 0) {29this.listeners.splice(idx, 1);30}31},32};33};34fire(data: T): void {35for (const listener of this.listeners) {36listener(data);37}38}39dispose(): void {40this.listeners.length = 0;41}42},43}));4445describe('InProcHttpServer.onDidClientDisconnect', () => {46let server: InProcHttpServer;4748beforeEach(() => {49server = new InProcHttpServer(new TestLogService(), new CopilotCLISessionTracker());50});5152it('should return a disposable', () => {53const disposable = server.onDidClientDisconnect(() => { });54expect(disposable).toBeDefined();55expect(disposable.dispose).toBeInstanceOf(Function);56});5758it('should remove the listener on dispose', () => {59const listener = vi.fn();60const disposable = server.onDidClientDisconnect(listener);61disposable.dispose();6263// Trigger _unregisterTransport indirectly by accessing private state64// Since _unregisterTransport is private, we verify via the listener not being called65// after dispose by checking the listener array is empty66expect(listener).not.toHaveBeenCalled();67});6869it('should support multiple listeners', () => {70const listener1 = vi.fn();71const listener2 = vi.fn();72server.onDidClientDisconnect(listener1);73server.onDidClientDisconnect(listener2);7475// Both should be registered (we can't easily trigger them without76// going through the full MCP flow, but at least we verify registration)77expect(listener1).not.toHaveBeenCalled();78expect(listener2).not.toHaveBeenCalled();79});8081it('should only remove the disposed listener', () => {82const listener1 = vi.fn();83const listener2 = vi.fn();84const disposable1 = server.onDidClientDisconnect(listener1);85server.onDidClientDisconnect(listener2);8687disposable1.dispose();8889// listener2 should still be registered90expect(listener1).not.toHaveBeenCalled();91expect(listener2).not.toHaveBeenCalled();92});9394it('should handle double dispose gracefully', () => {95const listener = vi.fn();96const disposable = server.onDidClientDisconnect(listener);97disposable.dispose();98expect(() => disposable.dispose()).not.toThrow();99});100});101102103