diff --git a/src/vs/workbench/contrib/chat/test/browser/agentSessions/localAgentSessionsController.test.ts b/src/vs/workbench/contrib/chat/test/browser/agentSessions/localAgentSessionsController.test.ts index f5ede2843bf..fdc31ae26eb 100644 --- a/src/vs/workbench/contrib/chat/test/browser/agentSessions/localAgentSessionsController.test.ts +++ b/src/vs/workbench/contrib/chat/test/browser/agentSessions/localAgentSessionsController.test.ts @@ -6,21 +6,21 @@ import assert from 'assert'; import { CancellationToken } from '../../../../../../base/common/cancellation.js'; import { Codicon } from '../../../../../../base/common/codicons.js'; -import { Emitter, Event } from '../../../../../../base/common/event.js'; -import { DisposableStore, IDisposable } from '../../../../../../base/common/lifecycle.js'; -import { ISettableObservable, observableValue } from '../../../../../../base/common/observable.js'; +import { Emitter } from '../../../../../../base/common/event.js'; +import { DisposableStore } from '../../../../../../base/common/lifecycle.js'; +import { observableValue } from '../../../../../../base/common/observable.js'; import { URI } from '../../../../../../base/common/uri.js'; import { runWithFakedTimers } from '../../../../../../base/test/common/timeTravelScheduler.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../../base/test/common/utils.js'; import { TestInstantiationService } from '../../../../../../platform/instantiation/test/common/instantiationServiceMock.js'; import { workbenchInstantiationService } from '../../../../../test/browser/workbenchTestServices.js'; import { LocalAgentsSessionsController } from '../../../browser/agentSessions/localAgentSessionsController.js'; +import { IChatService, ResponseModelState } from '../../../common/chatService/chatService.js'; +import { ChatSessionStatus, IChatSessionItem, IChatSessionsService, localChatSessionType } from '../../../common/chatSessionsService.js'; import { ModifiedFileEntryState } from '../../../common/editing/chatEditingService.js'; import { IChatModel, IChatRequestModel, IChatResponseModel } from '../../../common/model/chatModel.js'; -import { ChatRequestQueueKind, IChatDetail, IChatQuestionAnswers, IChatService, IChatSessionStartOptions, ResponseModelState } from '../../../common/chatService/chatService.js'; -import { ChatSessionStatus, IChatSessionItem, IChatSessionsService, localChatSessionType } from '../../../common/chatSessionsService.js'; import { LocalChatSessionUri } from '../../../common/model/chatUri.js'; -import { ChatAgentLocation } from '../../../common/constants.js'; +import { MockChatService } from '../../common/chatService/mockChatService.js'; import { MockChatSessionsService } from '../../common/mockChatSessionsService.js'; function createTestTiming(options?: { @@ -36,204 +36,6 @@ function createTestTiming(options?: { }; } -class MockChatService implements IChatService { - private readonly _chatModels: ISettableObservable> = observableValue('chatModels', []); - readonly chatModels = this._chatModels; - requestInProgressObs = observableValue('name', false); - _serviceBrand: undefined; - editingSessions = []; - transferredSessionResource = undefined; - readonly onDidSubmitRequest = Event.None; - readonly onDidCreateModel = Event.None; - - private sessions = new Map(); - private liveSessionItems: IChatDetail[] = []; - private historySessionItems: IChatDetail[] = []; - - private readonly _onDidDisposeSession = new Emitter<{ sessionResource: URI[]; reason: 'cleared' }>(); - readonly onDidDisposeSession = this._onDidDisposeSession.event; - - fireDidDisposeSession(sessionResource: URI[]): void { - this._onDidDisposeSession.fire({ sessionResource, reason: 'cleared' }); - } - - setSaveModelsEnabled(enabled: boolean): void { - - } - - processPendingRequests(sessionResource: URI): void { - - } - - setLiveSessionItems(items: IChatDetail[]): void { - this.liveSessionItems = items; - } - - setHistorySessionItems(items: IChatDetail[]): void { - this.historySessionItems = items; - } - - addSession(sessionResource: URI, session: IChatModel): void { - this.sessions.set(sessionResource.toString(), session); - // Update the chatModels observable - this._chatModels.set([...this.sessions.values()], undefined); - } - - removeSession(sessionResource: URI): void { - this.sessions.delete(sessionResource.toString()); - // Update the chatModels observable - this._chatModels.set([...this.sessions.values()], undefined); - } - - isEnabled(_location: ChatAgentLocation): boolean { - return true; - } - - hasSessions(): boolean { - return this.sessions.size > 0; - } - - getProviderInfos() { - return []; - } - - startNewLocalSession(_location: ChatAgentLocation, _options?: IChatSessionStartOptions): any { - throw new Error('Method not implemented.'); - } - - getSession(sessionResource: URI): IChatModel | undefined { - return this.sessions.get(sessionResource.toString()); - } - - getLatestRequest(): IChatRequestModel | undefined { - return undefined; - } - - acquireOrRestoreSession(_sessionResource: URI): Promise { - throw new Error('Method not implemented.'); - } - - getSessionTitle(_sessionResource: URI): string | undefined { - return undefined; - } - - loadSessionFromData(_data: any): any { - throw new Error('Method not implemented.'); - } - - acquireOrLoadSession(_resource: URI, _position: ChatAgentLocation, _token: CancellationToken): Promise { - throw new Error('Method not implemented.'); - } - - acquireExistingSession(_sessionResource: URI): any { - return undefined; - } - - setSessionTitle(_sessionResource: URI, _title: string): void { } - - appendProgress(_request: IChatRequestModel, _progress: any): void { } - - sendRequest(_sessionResource: URI, _message: string): Promise { - throw new Error('Method not implemented.'); - } - - resendRequest(_request: IChatRequestModel, _options?: any): Promise { - throw new Error('Method not implemented.'); - } - - adoptRequest(_sessionResource: URI, _request: IChatRequestModel): Promise { - throw new Error('Method not implemented.'); - } - - removeRequest(_sessionResource: URI, _requestId: string): Promise { - throw new Error('Method not implemented.'); - } - - async cancelCurrentRequestForSession(_sessionResource: URI, _source?: string): Promise { } - - setYieldRequested(_sessionResource: URI): void { } - - removePendingRequest(_sessionResource: URI, _requestId: string): void { } - - setPendingRequests(_sessionResource: URI, _requests: readonly { requestId: string; kind: ChatRequestQueueKind }[]): void { } - - addCompleteRequest(): void { } - - async getLocalSessionHistory(): Promise { - return this.historySessionItems; - } - - async clearAllHistoryEntries(): Promise { } - - async removeHistoryEntry(_resource: URI): Promise { } - - readonly onDidPerformUserAction = Event.None; - - notifyUserAction(_event: any): void { } - - readonly onDidReceiveQuestionCarouselAnswer = Event.None; - - notifyQuestionCarouselAnswer(_requestId: string, _resolveId: string, _answers: IChatQuestionAnswers | undefined): void { } - - async transferChatSession(): Promise { } - - setChatSessionTitle(): void { } - - isEditingLocation(_location: ChatAgentLocation): boolean { - return false; - } - - getChatStorageFolder(): URI { - return URI.file('/tmp'); - } - - logChatIndex(): void { } - - activateDefaultAgent(_location: ChatAgentLocation): Promise { - return Promise.resolve(); - } - - getChatSessionFromInternalUri(_sessionResource: URI): any { - return undefined; - } - - async getLiveSessionItems(): Promise { - return this.liveSessionItems; - } - - async getHistorySessionItems(): Promise { - return this.historySessionItems; - } - - waitForModelDisposals(): Promise { - return Promise.resolve(); - } - - getMetadataForSession(sessionResource: URI): Promise { - throw new Error('Method not implemented.'); - } - - - private onChange?: () => void; - - registerChatModelChangeListeners(chatSessionType: string, onChange: () => void): IDisposable { - // Store the emitter so tests can trigger it - this.onChange = onChange; - return { - dispose: () => { - this.onChange = undefined; - } - }; - } - - // Helper method for tests to trigger progress events - triggerProgressEvent(): void { - if (this.onChange) { - this.onChange(); - } - } -} - function createMockChatModel(options: { sessionResource: URI; hasRequests?: boolean; @@ -364,7 +166,7 @@ suite('LocalAgentsSessionsController', () => { timestamp: Date.now() }); - mockChatService.addSession(sessionResource, mockModel); + mockChatService.addSession(mockModel); mockChatService.setLiveSessionItems([{ sessionResource, title: 'Test Session', @@ -415,7 +217,7 @@ suite('LocalAgentsSessionsController', () => { hasRequests: true }); - mockChatService.addSession(sessionResource, mockModel); + mockChatService.addSession(mockModel); mockChatService.setLiveSessionItems([{ sessionResource, title: 'Live Session', @@ -452,7 +254,7 @@ suite('LocalAgentsSessionsController', () => { requestInProgress: true }); - mockChatService.addSession(sessionResource, mockModel); + mockChatService.addSession(mockModel); mockChatService.setLiveSessionItems([{ sessionResource, title: 'In Progress Session', @@ -483,7 +285,7 @@ suite('LocalAgentsSessionsController', () => { lastResponseHasError: false }); - mockChatService.addSession(sessionResource, mockModel); + mockChatService.addSession(mockModel); mockChatService.setLiveSessionItems([{ sessionResource, title: 'Completed Session', @@ -513,7 +315,7 @@ suite('LocalAgentsSessionsController', () => { lastResponseCanceled: true }); - mockChatService.addSession(sessionResource, mockModel); + mockChatService.addSession(mockModel); mockChatService.setLiveSessionItems([{ sessionResource, title: 'Canceled Session', @@ -543,7 +345,7 @@ suite('LocalAgentsSessionsController', () => { lastResponseHasError: true }); - mockChatService.addSession(sessionResource, mockModel); + mockChatService.addSession(mockModel); mockChatService.setLiveSessionItems([{ sessionResource, title: 'Error Session', @@ -588,7 +390,7 @@ suite('LocalAgentsSessionsController', () => { } }); - mockChatService.addSession(sessionResource, mockModel); + mockChatService.addSession(mockModel); mockChatService.setLiveSessionItems([{ sessionResource, title: 'Stats Session', @@ -634,7 +436,7 @@ suite('LocalAgentsSessionsController', () => { } }); - mockChatService.addSession(sessionResource, mockModel); + mockChatService.addSession(mockModel); mockChatService.setLiveSessionItems([{ sessionResource, title: 'No Stats Session', @@ -665,7 +467,7 @@ suite('LocalAgentsSessionsController', () => { timestamp: modelTimestamp }); - mockChatService.addSession(sessionResource, mockModel); + mockChatService.addSession(mockModel); mockChatService.setLiveSessionItems([{ sessionResource, title: 'Timing Session', @@ -719,7 +521,7 @@ suite('LocalAgentsSessionsController', () => { lastResponseCompletedAt: completedAt }); - mockChatService.addSession(sessionResource, mockModel); + mockChatService.addSession(mockModel); mockChatService.setLiveSessionItems([{ sessionResource, title: 'EndTime Session', @@ -748,7 +550,7 @@ suite('LocalAgentsSessionsController', () => { hasRequests: true }); - mockChatService.addSession(sessionResource, mockModel); + mockChatService.addSession(mockModel); mockChatService.setLiveSessionItems([{ sessionResource, title: 'Icon Session', @@ -779,7 +581,7 @@ suite('LocalAgentsSessionsController', () => { }); // Add the session first - mockChatService.addSession(sessionResource, mockModel); + mockChatService.addSession(mockModel); let changeEventCount = 0; disposables.add(controller.onDidChangeChatSessionItems(() => { @@ -805,7 +607,7 @@ suite('LocalAgentsSessionsController', () => { }); // Add the session first - mockChatService.addSession(sessionResource, mockModel); + mockChatService.addSession(mockModel); let changeEventCount = 0; disposables.add(controller.onDidChangeChatSessionItems(() => { @@ -830,7 +632,7 @@ suite('LocalAgentsSessionsController', () => { }); // Add the session first - mockChatService.addSession(sessionResource, mockModel); + mockChatService.addSession(mockModel); // Now remove the session - the observable should trigger cleanup mockChatService.removeSession(sessionResource); diff --git a/src/vs/workbench/contrib/chat/test/common/chatService/mockChatService.ts b/src/vs/workbench/contrib/chat/test/common/chatService/mockChatService.ts index ca141665f03..e268a3a137e 100644 --- a/src/vs/workbench/contrib/chat/test/common/chatService/mockChatService.ts +++ b/src/vs/workbench/contrib/chat/test/common/chatService/mockChatService.ts @@ -4,168 +4,192 @@ *--------------------------------------------------------------------------------------------*/ import { CancellationToken } from '../../../../../../base/common/cancellation.js'; -import { Event } from '../../../../../../base/common/event.js'; -import { ResourceMap } from '../../../../../../base/common/map.js'; -import { IObservable, observableValue } from '../../../../../../base/common/observable.js'; -import { URI } from '../../../../../../base/common/uri.js'; -import { IChatModel, IChatRequestModel, IChatRequestVariableData, ISerializableChatData } from '../../../common/model/chatModel.js'; -import { IParsedChatRequest } from '../../../common/requestParser/chatParserTypes.js'; -import { ChatRequestQueueKind, ChatSendResult, IChatCompleteResponse, IChatDetail, IChatModelReference, IChatProgress, IChatProviderInfo, IChatQuestionAnswers, IChatSendRequestOptions, IChatService, IChatSessionContext, IChatSessionStartOptions, IChatUserActionEvent } from '../../../common/chatService/chatService.js'; -import { ChatAgentLocation } from '../../../common/constants.js'; +import { Emitter, Event } from '../../../../../../base/common/event.js'; import { IDisposable } from '../../../../../../base/common/lifecycle.js'; +import { ISettableObservable, observableValue } from '../../../../../../base/common/observable.js'; +import { URI } from '../../../../../../base/common/uri.js'; +import { ChatRequestQueueKind, ChatSendResult, IChatDetail, IChatModelReference, IChatProgress, IChatSendRequestOptions, IChatService, IChatSessionContext, IChatSessionStartOptions, IChatUserActionEvent } from '../../../common/chatService/chatService.js'; +import { ChatAgentLocation } from '../../../common/constants.js'; +import { IChatModel, IChatRequestModel, IExportableChatData, ISerializableChatData } from '../../../common/model/chatModel.js'; export class MockChatService implements IChatService { - chatModels: IObservable> = observableValue('chatModels', []); + private readonly _chatModels: ISettableObservable> = observableValue('chatModels', []); + readonly chatModels = this._chatModels; requestInProgressObs = observableValue('name', false); _serviceBrand: undefined; editingSessions = []; - transferredSessionResource: URI | undefined; - readonly onDidSubmitRequest: Event<{ readonly chatSessionResource: URI; readonly message?: IParsedChatRequest }> = Event.None; - readonly onDidCreateModel: Event = Event.None; + transferredSessionResource = undefined; + readonly onDidSubmitRequest = Event.None; + readonly onDidCreateModel = Event.None; - private sessions = new ResourceMap(); + private sessions = new Map(); + private liveSessionItems: IChatDetail[] = []; + private historySessionItems: IChatDetail[] = []; + + private readonly _onDidDisposeSession = new Emitter<{ sessionResource: URI[]; reason: 'cleared' }>(); + readonly onDidDisposeSession = this._onDidDisposeSession.event; + + fireDidDisposeSession(sessionResource: URI[]): void { + this._onDidDisposeSession.fire({ sessionResource, reason: 'cleared' }); + } setSaveModelsEnabled(enabled: boolean): void { } - isEnabled(location: ChatAgentLocation): boolean { - throw new Error('Method not implemented.'); - } - hasSessions(): boolean { - throw new Error('Method not implemented.'); - } - getProviderInfos(): IChatProviderInfo[] { - throw new Error('Method not implemented.'); - } - startNewLocalSession(location: ChatAgentLocation, options?: IChatSessionStartOptions): IChatModelReference { - throw new Error('Method not implemented.'); - } - addSession(session: IChatModel): void { - this.sessions.set(session.sessionResource, session); - } - getSession(sessionResource: URI): IChatModel | undefined { - // eslint-disable-next-line local/code-no-dangerous-type-assertions - return this.sessions.get(sessionResource) ?? {} as IChatModel; - } - getLatestRequest(): IChatRequestModel | undefined { - return undefined; - } - async acquireOrRestoreSession(sessionResource: URI): Promise { - throw new Error('Method not implemented.'); - } - getSessionTitle(sessionResource: URI): string | undefined { - throw new Error('Method not implemented.'); - } - loadSessionFromData(data: ISerializableChatData): IChatModelReference { - throw new Error('Method not implemented.'); - } - acquireOrLoadSession(resource: URI, position: ChatAgentLocation, token: CancellationToken): Promise { - throw new Error('Method not implemented.'); - } - acquireExistingSession(sessionResource: URI): IChatModelReference | undefined { - return undefined; - } - setSessionTitle(sessionResource: URI, title: string): void { - throw new Error('Method not implemented.'); - } - appendProgress(request: IChatRequestModel, progress: IChatProgress): void { - } processPendingRequests(sessionResource: URI): void { } - /** - * Returns whether the request was accepted. - */ - sendRequest(sessionResource: URI, message: string): Promise { + + setLiveSessionItems(items: IChatDetail[]): void { + this.liveSessionItems = items; + } + + setHistorySessionItems(items: IChatDetail[]): void { + this.historySessionItems = items; + } + + addSession(session: IChatModel): void { + this.sessions.set(session.sessionResource.toString(), session); + // Update the chatModels observable + this._chatModels.set([...this.sessions.values()], undefined); + } + + removeSession(sessionResource: URI): void { + this.sessions.delete(sessionResource.toString()); + // Update the chatModels observable + this._chatModels.set([...this.sessions.values()], undefined); + } + + isEnabled(_location: ChatAgentLocation): boolean { + return true; + } + + hasSessions(): boolean { + return this.sessions.size > 0; + } + + getProviderInfos() { + return []; + } + + startNewLocalSession(_location: ChatAgentLocation, _options?: IChatSessionStartOptions): IChatModelReference { throw new Error('Method not implemented.'); } - resendRequest(request: IChatRequestModel, options?: IChatSendRequestOptions | undefined): Promise { + + getSession(sessionResource: URI): IChatModel | undefined { + return this.sessions.get(sessionResource.toString()); + } + + getLatestRequest(): IChatRequestModel | undefined { + return undefined; + } + + acquireOrRestoreSession(_sessionResource: URI): Promise { throw new Error('Method not implemented.'); } - adoptRequest(sessionResource: URI, request: IChatRequestModel): Promise { + + getSessionTitle(_sessionResource: URI): string | undefined { + return undefined; + } + + loadSessionFromData(data: IExportableChatData | ISerializableChatData): IChatModelReference { throw new Error('Method not implemented.'); } - removeRequest(sessionResource: URI, requestId: string): Promise { + + acquireOrLoadSession(_resource: URI, _position: ChatAgentLocation, _token: CancellationToken): Promise { throw new Error('Method not implemented.'); } - async cancelCurrentRequestForSession(sessionResource: URI, source?: string): Promise { + + acquireExistingSession(_sessionResource: URI): IChatModelReference | undefined { + return undefined; + } + + setSessionTitle(_sessionResource: URI, _title: string): void { } + + appendProgress(_request: IChatRequestModel, _progress: IChatProgress): void { } + + sendRequest(_sessionResource: URI, _message: string): Promise { throw new Error('Method not implemented.'); } - setYieldRequested(sessionResource: URI): void { + + resendRequest(_request: IChatRequestModel, _options?: IChatSendRequestOptions): Promise { throw new Error('Method not implemented.'); } - removePendingRequest(sessionResource: URI, requestId: string): void { + + adoptRequest(_sessionResource: URI, _request: IChatRequestModel): Promise { throw new Error('Method not implemented.'); } - setPendingRequests(sessionResource: URI, requests: readonly { requestId: string; kind: ChatRequestQueueKind }[]): void { - throw new Error('Method not implemented.'); - } - addCompleteRequest(sessionResource: URI, message: IParsedChatRequest | string, variableData: IChatRequestVariableData | undefined, attempt: number | undefined, response: IChatCompleteResponse): void { + + removeRequest(_sessionResource: URI, _requestId: string): Promise { throw new Error('Method not implemented.'); } + + async cancelCurrentRequestForSession(_sessionResource: URI, _source?: string): Promise { } + + setYieldRequested(_sessionResource: URI): void { } + + removePendingRequest(_sessionResource: URI, _requestId: string): void { } + + setPendingRequests(_sessionResource: URI, _requests: readonly { requestId: string; kind: ChatRequestQueueKind }[]): void { } + + addCompleteRequest(): void { } + async getLocalSessionHistory(): Promise { - throw new Error('Method not implemented.'); - } - async clearAllHistoryEntries() { - throw new Error('Method not implemented.'); - } - async removeHistoryEntry(resource: URI) { - throw new Error('Method not implemented.'); + return this.historySessionItems; } - readonly onDidPerformUserAction: Event = undefined!; - notifyUserAction(event: IChatUserActionEvent): void { - throw new Error('Method not implemented.'); - } - readonly onDidReceiveQuestionCarouselAnswer: Event<{ requestId: string; resolveId: string; answers: IChatQuestionAnswers | undefined }> = undefined!; - notifyQuestionCarouselAnswer(requestId: string, resolveId: string, answers: IChatQuestionAnswers | undefined): void { - throw new Error('Method not implemented.'); - } - readonly onDidDisposeSession: Event<{ sessionResource: URI[]; reason: 'cleared' }> = undefined!; + async clearAllHistoryEntries(): Promise { } - async transferChatSession(transferredSessionResource: URI, toWorkspace: URI): Promise { - throw new Error('Method not implemented.'); - } + async removeHistoryEntry(_resource: URI): Promise { } - setChatSessionTitle(sessionResource: URI, title: string): void { - throw new Error('Method not implemented.'); - } + readonly onDidPerformUserAction = Event.None; - isEditingLocation(location: ChatAgentLocation): boolean { - throw new Error('Method not implemented.'); + notifyUserAction(_event: IChatUserActionEvent): void { } + + readonly onDidReceiveQuestionCarouselAnswer = Event.None; + + notifyQuestionCarouselAnswer(_requestId: string, _resolveId: string, _answers: Record | undefined): void { } + + async transferChatSession(): Promise { } + + setChatSessionTitle(): void { } + + isEditingLocation(_location: ChatAgentLocation): boolean { + return false; } getChatStorageFolder(): URI { - throw new Error('Method not implemented.'); + return URI.file('/tmp'); } - logChatIndex(): void { - throw new Error('Method not implemented.'); + logChatIndex(): void { } + + activateDefaultAgent(_location: ChatAgentLocation): Promise { + return Promise.resolve(); } - activateDefaultAgent(location: ChatAgentLocation): Promise { - throw new Error('Method not implemented.'); - } - - getChatSessionFromInternalUri(sessionResource: URI): IChatSessionContext | undefined { - throw new Error('Method not implemented.'); + getChatSessionFromInternalUri(_sessionResource: URI): IChatSessionContext | undefined { + return undefined; } async getLiveSessionItems(): Promise { - throw new Error('Method not implemented.'); + return this.liveSessionItems; } - getHistorySessionItems(): Promise { - throw new Error('Method not implemented.'); + + async getHistorySessionItems(): Promise { + return this.historySessionItems; } waitForModelDisposals(): Promise { - throw new Error('Method not implemented.'); + return Promise.resolve(); } + getMetadataForSession(sessionResource: URI): Promise { throw new Error('Method not implemented.'); } + private onChange?: () => void; registerChatModelChangeListeners(chatSessionType: string, onChange: () => void): IDisposable {