Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/chatSessions/copilotcli/vscode-node/test/closeDiff.spec.ts
13406 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { beforeEach, describe, expect, it, vi } from 'vitest';
7
import { TestLogService } from '../../../../../platform/testing/common/testLogService';
8
import { MockMcpServer, parseToolResult } from './testHelpers';
9
10
vi.mock('vscode', () => ({
11
Uri: {
12
file: (path: string) => ({ fsPath: path, scheme: 'file' }),
13
},
14
window: {
15
tabGroups: {
16
activeTabGroup: {
17
activeTab: null,
18
},
19
all: [],
20
onDidChangeTabGroups: () => ({ dispose: () => { } }),
21
onDidChangeTabs: () => ({ dispose: () => { } }),
22
},
23
},
24
commands: {
25
executeCommand: vi.fn().mockResolvedValue(undefined),
26
},
27
TabInputTextDiff: class TabInputTextDiff {
28
constructor(public original: any, public modified: any) { }
29
},
30
}));
31
32
interface CloseDiffResult {
33
success: boolean;
34
already_closed: boolean;
35
tab_name: string;
36
message: string;
37
}
38
39
import {
40
DiffStateManager,
41
type ActiveDiff,
42
} from '../diffState';
43
import { registerCloseDiffTool } from '../tools/closeDiff';
44
45
describe('closeDiff tool', () => {
46
const logger = new TestLogService();
47
let diffState: DiffStateManager;
48
49
const createMockDiff = (tabName: string, diffIdSuffix?: string): ActiveDiff => ({
50
diffId: `/tmp/modified-${diffIdSuffix ?? tabName}.ts`,
51
tabName: tabName,
52
originalUri: { fsPath: `/path/to/original-${tabName}.ts`, scheme: 'file' } as any,
53
modifiedUri: { fsPath: `/tmp/modified-${diffIdSuffix ?? tabName}.ts`, scheme: 'file' } as any,
54
newContents: `// new contents for ${tabName}`,
55
cleanup: vi.fn(),
56
resolve: vi.fn(),
57
});
58
59
beforeEach(() => {
60
diffState = new DiffStateManager(logger);
61
});
62
63
it('should register the close_diff tool', () => {
64
const mockServer = new MockMcpServer();
65
registerCloseDiffTool(mockServer as any, logger, diffState);
66
67
expect(mockServer.getToolHandler('close_diff')).toBeDefined();
68
});
69
70
it('should close an active diff by tab name', async () => {
71
const mockServer = new MockMcpServer();
72
registerCloseDiffTool(mockServer as any, logger, diffState);
73
74
const diff = createMockDiff('My Test Diff');
75
diffState.register(diff);
76
77
const handler = mockServer.getToolHandler('close_diff')!;
78
const result = await handler({ tab_name: 'My Test Diff' });
79
const parsed = parseToolResult<CloseDiffResult>(result);
80
81
expect(parsed.success).toBe(true);
82
expect(parsed.already_closed).toBe(false);
83
expect(parsed.tab_name).toBe('My Test Diff');
84
expect(parsed.message).toContain('closed successfully');
85
86
expect(diff.resolve).toHaveBeenCalledWith({
87
status: 'REJECTED',
88
trigger: 'closed_via_tool',
89
});
90
});
91
92
it('should return success with already_closed=true for non-existent tab', async () => {
93
const mockServer = new MockMcpServer();
94
registerCloseDiffTool(mockServer as any, logger, diffState);
95
96
const handler = mockServer.getToolHandler('close_diff')!;
97
const result = await handler({ tab_name: 'Non-existent Tab' });
98
const parsed = parseToolResult<CloseDiffResult>(result);
99
100
expect(parsed.success).toBe(true);
101
expect(parsed.already_closed).toBe(true);
102
expect(parsed.tab_name).toBe('Non-existent Tab');
103
expect(parsed.message).toContain('may already be closed');
104
});
105
106
it('should be idempotent - closing same tab twice returns success', async () => {
107
const mockServer = new MockMcpServer();
108
registerCloseDiffTool(mockServer as any, logger, diffState);
109
110
const diff = createMockDiff('Idempotent Test');
111
diffState.register(diff);
112
113
const handler = mockServer.getToolHandler('close_diff')!;
114
115
const result1 = await handler({ tab_name: 'Idempotent Test' });
116
const parsed1 = parseToolResult<CloseDiffResult>(result1);
117
expect(parsed1.success).toBe(true);
118
expect(parsed1.already_closed).toBe(false);
119
120
diffState.unregister(diff.diffId);
121
122
const result2 = await handler({ tab_name: 'Idempotent Test' });
123
const parsed2 = parseToolResult<CloseDiffResult>(result2);
124
expect(parsed2.success).toBe(true);
125
expect(parsed2.already_closed).toBe(true);
126
});
127
128
it('should close the correct diff when multiple diffs are open', async () => {
129
const mockServer = new MockMcpServer();
130
registerCloseDiffTool(mockServer as any, logger, diffState);
131
132
const diff1 = createMockDiff('First Diff');
133
const diff2 = createMockDiff('Second Diff');
134
const diff3 = createMockDiff('Third Diff');
135
diffState.register(diff1);
136
diffState.register(diff2);
137
diffState.register(diff3);
138
139
const handler = mockServer.getToolHandler('close_diff')!;
140
141
const result = await handler({ tab_name: 'Second Diff' });
142
const parsed = parseToolResult<CloseDiffResult>(result);
143
144
expect(parsed.success).toBe(true);
145
expect(parsed.already_closed).toBe(false);
146
147
expect(diff1.resolve).not.toHaveBeenCalled();
148
expect(diff2.resolve).toHaveBeenCalledWith({
149
status: 'REJECTED',
150
trigger: 'closed_via_tool',
151
});
152
expect(diff3.resolve).not.toHaveBeenCalled();
153
154
expect(diffState.getByTabName('First Diff')).toBe(diff1);
155
expect(diffState.getByTabName('Third Diff')).toBe(diff3);
156
});
157
158
describe('edge cases', () => {
159
it('should handle empty tab name', async () => {
160
const mockServer = new MockMcpServer();
161
registerCloseDiffTool(mockServer as any, logger, diffState);
162
163
const handler = mockServer.getToolHandler('close_diff')!;
164
const result = await handler({ tab_name: '' });
165
const parsed = parseToolResult<CloseDiffResult>(result);
166
167
expect(parsed.success).toBe(true);
168
expect(parsed.already_closed).toBe(true);
169
});
170
171
it('should handle tab name with special characters', async () => {
172
const mockServer = new MockMcpServer();
173
registerCloseDiffTool(mockServer as any, logger, diffState);
174
175
const diff = createMockDiff('Diff: src/file.ts → modified (2024-01-23)');
176
diffState.register(diff);
177
178
const handler = mockServer.getToolHandler('close_diff')!;
179
const result = await handler({ tab_name: 'Diff: src/file.ts → modified (2024-01-23)' });
180
const parsed = parseToolResult<CloseDiffResult>(result);
181
182
expect(parsed.success).toBe(true);
183
expect(parsed.already_closed).toBe(false);
184
expect(diff.resolve).toHaveBeenCalled();
185
});
186
187
it('should handle closing tab that was never opened', async () => {
188
const mockServer = new MockMcpServer();
189
registerCloseDiffTool(mockServer as any, logger, diffState);
190
191
const handler = mockServer.getToolHandler('close_diff')!;
192
const result = await handler({ tab_name: 'Never Existed Tab' });
193
const parsed = parseToolResult<CloseDiffResult>(result);
194
195
expect(parsed.success).toBe(true);
196
expect(parsed.already_closed).toBe(true);
197
expect(parsed.message).toContain('may already be closed');
198
});
199
200
it('should handle multiple diffs with same tab name but different diffIds', async () => {
201
const mockServer = new MockMcpServer();
202
registerCloseDiffTool(mockServer as any, logger, diffState);
203
204
const diff1 = createMockDiff('Duplicate Name', 'diff1');
205
const diff2 = createMockDiff('Duplicate Name', 'diff2');
206
diffState.register(diff1);
207
diffState.register(diff2);
208
209
const handler = mockServer.getToolHandler('close_diff')!;
210
const result = await handler({ tab_name: 'Duplicate Name' });
211
const parsed = parseToolResult<CloseDiffResult>(result);
212
213
expect(parsed.success).toBe(true);
214
expect(parsed.already_closed).toBe(false);
215
216
expect(diff1.resolve).toHaveBeenCalled();
217
expect(diff2.resolve).not.toHaveBeenCalled();
218
219
diffState.unregister(diff1.diffId);
220
221
expect(diffState.getByTabName('Duplicate Name')).toBe(diff2);
222
});
223
224
it('should handle rapid successive closes of different tabs', async () => {
225
const mockServer = new MockMcpServer();
226
registerCloseDiffTool(mockServer as any, logger, diffState);
227
228
const diff1 = createMockDiff('Tab A');
229
const diff2 = createMockDiff('Tab B');
230
const diff3 = createMockDiff('Tab C');
231
diffState.register(diff1);
232
diffState.register(diff2);
233
diffState.register(diff3);
234
235
const handler = mockServer.getToolHandler('close_diff')!;
236
237
const [result1, result2, result3] = await Promise.all([
238
handler({ tab_name: 'Tab A' }),
239
handler({ tab_name: 'Tab B' }),
240
handler({ tab_name: 'Tab C' }),
241
]);
242
243
expect(parseToolResult<CloseDiffResult>(result1).success).toBe(true);
244
expect(parseToolResult<CloseDiffResult>(result2).success).toBe(true);
245
expect(parseToolResult<CloseDiffResult>(result3).success).toBe(true);
246
247
expect(diff1.resolve).toHaveBeenCalled();
248
expect(diff2.resolve).toHaveBeenCalled();
249
expect(diff3.resolve).toHaveBeenCalled();
250
});
251
252
it('should handle tab name that is very long', async () => {
253
const mockServer = new MockMcpServer();
254
registerCloseDiffTool(mockServer as any, logger, diffState);
255
256
const longTabName = 'A'.repeat(1000);
257
const diff = createMockDiff(longTabName);
258
diffState.register(diff);
259
260
const handler = mockServer.getToolHandler('close_diff')!;
261
const result = await handler({ tab_name: longTabName });
262
const parsed = parseToolResult<CloseDiffResult>(result);
263
264
expect(parsed.success).toBe(true);
265
expect(parsed.already_closed).toBe(false);
266
expect(diff.resolve).toHaveBeenCalled();
267
});
268
269
it('should handle whitespace-only tab name', async () => {
270
const mockServer = new MockMcpServer();
271
registerCloseDiffTool(mockServer as any, logger, diffState);
272
273
const handler = mockServer.getToolHandler('close_diff')!;
274
const result = await handler({ tab_name: ' ' });
275
const parsed = parseToolResult<CloseDiffResult>(result);
276
277
expect(parsed.success).toBe(true);
278
expect(parsed.already_closed).toBe(true);
279
});
280
281
it('should handle tab name with unicode characters', async () => {
282
const mockServer = new MockMcpServer();
283
registerCloseDiffTool(mockServer as any, logger, diffState);
284
285
const diff = createMockDiff('编辑文件 🔧 файл.ts');
286
diffState.register(diff);
287
288
const handler = mockServer.getToolHandler('close_diff')!;
289
const result = await handler({ tab_name: '编辑文件 🔧 файл.ts' });
290
const parsed = parseToolResult<CloseDiffResult>(result);
291
292
expect(parsed.success).toBe(true);
293
expect(parsed.already_closed).toBe(false);
294
expect(diff.resolve).toHaveBeenCalled();
295
});
296
});
297
});
298
299