Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/sessions/contrib/agentFeedback/test/browser/agentFeedbackService.test.ts
13405 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 assert from 'assert';
7
import { URI } from '../../../../../base/common/uri.js';
8
import { Range } from '../../../../../editor/common/core/range.js';
9
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';
10
import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js';
11
import { mock } from '../../../../../base/test/common/mock.js';
12
import { AgentFeedbackService, IAgentFeedbackService } from '../../browser/agentFeedbackService.js';
13
import { IChatEditingService } from '../../../../../workbench/contrib/chat/common/editing/chatEditingService.js';
14
import { IAgentSessionsService } from '../../../../../workbench/contrib/chat/browser/agentSessions/agentSessionsService.js';
15
import { DisposableStore } from '../../../../../base/common/lifecycle.js';
16
import { NullTelemetryService } from '../../../../../platform/telemetry/common/telemetryUtils.js';
17
import { ITelemetryService } from '../../../../../platform/telemetry/common/telemetry.js';
18
19
function r(startLine: number, endLine: number = startLine): Range {
20
return new Range(startLine, 1, endLine, 1);
21
}
22
23
function feedbackSummary(items: readonly { resourceUri: URI; range: { startLineNumber: number } }[]): string[] {
24
return items.map(f => `${f.resourceUri.path}:${f.range.startLineNumber}`);
25
}
26
27
suite('AgentFeedbackService - Ordering', () => {
28
29
const store = new DisposableStore();
30
let service: IAgentFeedbackService;
31
let session: URI;
32
let fileA: URI;
33
let fileB: URI;
34
let fileC: URI;
35
36
setup(() => {
37
const instantiationService = store.add(new TestInstantiationService());
38
39
instantiationService.stub(IChatEditingService, new class extends mock<IChatEditingService>() { });
40
instantiationService.stub(IAgentSessionsService, new class extends mock<IAgentSessionsService>() { });
41
instantiationService.stub(ITelemetryService, NullTelemetryService);
42
43
service = store.add(instantiationService.createInstance(AgentFeedbackService));
44
session = URI.parse('test://session/1');
45
fileA = URI.parse('file:///a.ts');
46
fileB = URI.parse('file:///b.ts');
47
fileC = URI.parse('file:///c.ts');
48
});
49
50
teardown(() => {
51
store.clear();
52
});
53
54
ensureNoDisposablesAreLeakedInTestSuite();
55
56
test('single file - items sorted by line number', () => {
57
service.addFeedback(session, fileA, r(20), 'line 20');
58
service.addFeedback(session, fileA, r(5), 'line 5');
59
service.addFeedback(session, fileA, r(10), 'line 10');
60
61
assert.deepStrictEqual(feedbackSummary(service.getFeedback(session)), [
62
'/a.ts:5',
63
'/a.ts:10',
64
'/a.ts:20',
65
]);
66
});
67
68
test('multiple files - files ordered by recency, items within file sorted by line', () => {
69
service.addFeedback(session, fileA, r(10), 'A:10');
70
service.addFeedback(session, fileA, r(5), 'A:5');
71
service.addFeedback(session, fileB, r(20), 'B:20');
72
service.addFeedback(session, fileB, r(3), 'B:3');
73
74
assert.deepStrictEqual(feedbackSummary(service.getFeedback(session)), [
75
'/a.ts:5',
76
'/a.ts:10',
77
'/b.ts:3',
78
'/b.ts:20',
79
]);
80
});
81
82
test('new file appended to end', () => {
83
service.addFeedback(session, fileA, r(1), 'A:1');
84
service.addFeedback(session, fileB, r(1), 'B:1');
85
service.addFeedback(session, fileC, r(1), 'C:1');
86
87
assert.deepStrictEqual(feedbackSummary(service.getFeedback(session)), [
88
'/a.ts:1',
89
'/b.ts:1',
90
'/c.ts:1',
91
]);
92
});
93
94
test('adding to existing file does not change file ordering', () => {
95
service.addFeedback(session, fileA, r(10), 'A:10');
96
service.addFeedback(session, fileB, r(10), 'B:10');
97
// Add more feedback to fileA — should stay before fileB
98
service.addFeedback(session, fileA, r(5), 'A:5');
99
service.addFeedback(session, fileA, r(20), 'A:20');
100
101
assert.deepStrictEqual(feedbackSummary(service.getFeedback(session)), [
102
'/a.ts:5',
103
'/a.ts:10',
104
'/a.ts:20',
105
'/b.ts:10',
106
]);
107
});
108
109
test('interleaved adds across files maintain file recency and line sort', () => {
110
service.addFeedback(session, fileA, r(30), 'A:30');
111
service.addFeedback(session, fileB, r(50), 'B:50');
112
service.addFeedback(session, fileA, r(10), 'A:10');
113
service.addFeedback(session, fileC, r(1), 'C:1');
114
service.addFeedback(session, fileB, r(5), 'B:5');
115
service.addFeedback(session, fileA, r(20), 'A:20');
116
117
assert.deepStrictEqual(feedbackSummary(service.getFeedback(session)), [
118
'/a.ts:10',
119
'/a.ts:20',
120
'/a.ts:30',
121
'/b.ts:5',
122
'/b.ts:50',
123
'/c.ts:1',
124
]);
125
});
126
127
test('navigation follows sorted order', () => {
128
service.addFeedback(session, fileA, r(20), 'A:20');
129
service.addFeedback(session, fileB, r(10), 'B:10');
130
service.addFeedback(session, fileA, r(5), 'A:5');
131
132
// Expected order: A:5, A:20, B:10
133
const first = service.getNextFeedback(session, true)!;
134
assert.strictEqual(first.resourceUri.path, '/a.ts');
135
assert.strictEqual(first.range.startLineNumber, 5);
136
137
const second = service.getNextFeedback(session, true)!;
138
assert.strictEqual(second.resourceUri.path, '/a.ts');
139
assert.strictEqual(second.range.startLineNumber, 20);
140
141
const third = service.getNextFeedback(session, true)!;
142
assert.strictEqual(third.resourceUri.path, '/b.ts');
143
assert.strictEqual(third.range.startLineNumber, 10);
144
145
// Wraps around
146
const fourth = service.getNextFeedback(session, true)!;
147
assert.strictEqual(fourth.resourceUri.path, '/a.ts');
148
assert.strictEqual(fourth.range.startLineNumber, 5);
149
});
150
151
test('navigation bearings reflect sorted position', () => {
152
service.addFeedback(session, fileA, r(20), 'A:20');
153
service.addFeedback(session, fileA, r(5), 'A:5');
154
service.addFeedback(session, fileB, r(1), 'B:1');
155
156
// Before navigation, no anchor
157
let bearing = service.getNavigationBearing(session);
158
assert.strictEqual(bearing.activeIdx, -1);
159
assert.strictEqual(bearing.totalCount, 3);
160
161
// Navigate to first (A:5)
162
service.getNextFeedback(session, true);
163
bearing = service.getNavigationBearing(session);
164
assert.strictEqual(bearing.activeIdx, 0);
165
166
// Navigate to second (A:20)
167
service.getNextFeedback(session, true);
168
bearing = service.getNavigationBearing(session);
169
assert.strictEqual(bearing.activeIdx, 1);
170
171
// Navigate to third (B:1)
172
service.getNextFeedback(session, true);
173
bearing = service.getNavigationBearing(session);
174
assert.strictEqual(bearing.activeIdx, 2);
175
});
176
177
test('removing feedback preserves ordering', () => {
178
const f1 = service.addFeedback(session, fileA, r(30), 'A:30');
179
service.addFeedback(session, fileA, r(10), 'A:10');
180
service.addFeedback(session, fileA, r(20), 'A:20');
181
182
assert.deepStrictEqual(feedbackSummary(service.getFeedback(session)), [
183
'/a.ts:10',
184
'/a.ts:20',
185
'/a.ts:30',
186
]);
187
188
service.removeFeedback(session, f1.id);
189
assert.deepStrictEqual(feedbackSummary(service.getFeedback(session)), [
190
'/a.ts:10',
191
'/a.ts:20',
192
]);
193
});
194
195
test('same line number items are stable', () => {
196
const f1 = service.addFeedback(session, fileA, r(10), 'first');
197
const f2 = service.addFeedback(session, fileA, r(10), 'second');
198
199
const items = service.getFeedback(session);
200
assert.strictEqual(items[0].id, f1.id);
201
assert.strictEqual(items[1].id, f2.id);
202
});
203
204
test('preserves optional feedback context fields', () => {
205
const feedback = service.addFeedback(session, fileA, r(10), 'with context', undefined, {
206
codeSelection: 'const value = 1;',
207
diffHunks: '@@ -1,1 +1,1 @@\n-const value = 0;\n+const value = 1;',
208
});
209
210
assert.strictEqual(feedback.codeSelection, 'const value = 1;');
211
assert.strictEqual(feedback.diffHunks, '@@ -1,1 +1,1 @@\n-const value = 0;\n+const value = 1;');
212
});
213
});
214
215