Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/platform/agentHost/test/node/sessionDataService.test.ts
13399 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 { VSBuffer } from '../../../../base/common/buffer.js';
8
import { DisposableStore } from '../../../../base/common/lifecycle.js';
9
import { Schemas } from '../../../../base/common/network.js';
10
import { URI } from '../../../../base/common/uri.js';
11
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';
12
import { FileService } from '../../../files/common/fileService.js';
13
import { InMemoryFileSystemProvider } from '../../../files/common/inMemoryFilesystemProvider.js';
14
import { NullLogService } from '../../../log/common/log.js';
15
import { AgentSession } from '../../common/agentService.js';
16
import { SessionDataService } from '../../node/sessionDataService.js';
17
18
suite('SessionDataService', () => {
19
20
const disposables = new DisposableStore();
21
let fileService: FileService;
22
let service: SessionDataService;
23
const basePath = URI.from({ scheme: Schemas.inMemory, path: '/userData' });
24
25
setup(() => {
26
fileService = disposables.add(new FileService(new NullLogService()));
27
disposables.add(fileService.registerProvider(Schemas.inMemory, disposables.add(new InMemoryFileSystemProvider())));
28
service = new SessionDataService(basePath, fileService, new NullLogService());
29
});
30
31
teardown(() => disposables.clear());
32
ensureNoDisposablesAreLeakedInTestSuite();
33
34
test('getSessionDataDir returns correct URI', () => {
35
const session = AgentSession.uri('copilot', 'abc-123');
36
const dir = service.getSessionDataDir(session);
37
assert.strictEqual(dir.toString(), URI.joinPath(basePath, 'agentSessionData', 'abc-123').toString());
38
});
39
40
test('getSessionDataDir sanitizes unsafe characters', () => {
41
const session = AgentSession.uri('copilot', 'foo/bar:baz\\qux');
42
const dir = service.getSessionDataDir(session);
43
assert.strictEqual(dir.toString(), URI.joinPath(basePath, 'agentSessionData', 'foo-bar-baz-qux').toString());
44
});
45
46
test('deleteSessionData removes directory', async () => {
47
const session = AgentSession.uri('copilot', 'session-1');
48
const dir = service.getSessionDataDir(session);
49
await fileService.createFolder(dir);
50
await fileService.writeFile(URI.joinPath(dir, 'snapshot.json'), VSBuffer.fromString('{}'));
51
52
assert.ok(await fileService.exists(dir));
53
await service.deleteSessionData(session);
54
assert.ok(!(await fileService.exists(dir)));
55
});
56
57
test('deleteSessionData is a no-op when directory does not exist', async () => {
58
const session = AgentSession.uri('copilot', 'nonexistent');
59
// Should not throw
60
await service.deleteSessionData(session);
61
});
62
63
test('cleanupOrphanedData deletes orphans but keeps known sessions', async () => {
64
const baseDir = URI.joinPath(basePath, 'agentSessionData');
65
await fileService.createFolder(URI.joinPath(baseDir, 'keep-1'));
66
await fileService.createFolder(URI.joinPath(baseDir, 'keep-2'));
67
await fileService.createFolder(URI.joinPath(baseDir, 'orphan-1'));
68
await fileService.createFolder(URI.joinPath(baseDir, 'orphan-2'));
69
70
await service.cleanupOrphanedData(new Set(['keep-1', 'keep-2']));
71
72
assert.ok(await fileService.exists(URI.joinPath(baseDir, 'keep-1')));
73
assert.ok(await fileService.exists(URI.joinPath(baseDir, 'keep-2')));
74
assert.ok(!(await fileService.exists(URI.joinPath(baseDir, 'orphan-1'))));
75
assert.ok(!(await fileService.exists(URI.joinPath(baseDir, 'orphan-2'))));
76
});
77
78
test('cleanupOrphanedData is a no-op when base directory does not exist', async () => {
79
// Should not throw
80
await service.cleanupOrphanedData(new Set());
81
});
82
});
83
84
suite('SessionDataService — openDatabase ref-counting', () => {
85
86
const disposables = new DisposableStore();
87
const basePath = URI.from({ scheme: Schemas.inMemory, path: '/userData' });
88
let service: SessionDataService;
89
90
setup(() => {
91
const fileService = disposables.add(new FileService(new NullLogService()));
92
disposables.add(fileService.registerProvider(Schemas.inMemory, disposables.add(new InMemoryFileSystemProvider())));
93
service = new SessionDataService(basePath, fileService, new NullLogService(), () => ':memory:');
94
});
95
96
teardown(() => {
97
disposables.clear();
98
});
99
ensureNoDisposablesAreLeakedInTestSuite();
100
101
test('returns a functional database reference', async () => {
102
const session = AgentSession.uri('copilot', 'ref-test');
103
const ref = service.openDatabase(session);
104
disposables.add(ref);
105
106
await ref.object.createTurn('turn-1');
107
const edits = await ref.object.getFileEdits([]);
108
assert.deepStrictEqual(edits, []);
109
await ref.object.close();
110
});
111
112
test('multiple references share the same database', async () => {
113
const session = AgentSession.uri('copilot', 'shared-test');
114
const ref1 = service.openDatabase(session);
115
const ref2 = service.openDatabase(session);
116
117
assert.strictEqual(ref1.object, ref2.object);
118
119
ref1.dispose();
120
ref2.dispose();
121
await ref1.object.close();
122
});
123
124
test('database remains usable until last reference is disposed', async () => {
125
const session = AgentSession.uri('copilot', 'refcount-test');
126
const ref1 = service.openDatabase(session);
127
const ref2 = service.openDatabase(session);
128
129
ref1.dispose();
130
131
// ref2 still works
132
await ref2.object.createTurn('turn-1');
133
134
ref2.dispose();
135
136
await ref1.object.close();
137
});
138
139
test('new reference after all disposed gets a fresh database', async () => {
140
const session = AgentSession.uri('copilot', 'reopen-test');
141
const ref1 = service.openDatabase(session);
142
const db1 = ref1.object;
143
ref1.dispose();
144
145
const ref2 = service.openDatabase(session);
146
disposables.add(ref2);
147
// New reference — may or may not be the same object, but must be functional
148
await ref2.object.createTurn('turn-1');
149
assert.notStrictEqual(ref2.object, db1);
150
151
await ref2.object.close();
152
});
153
});
154
155