Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/services/authentication/test/browser/authenticationQueryServiceMocks.ts
3296 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 { Emitter } from '../../../../../base/common/event.js';
7
import { Disposable, IDisposable } from '../../../../../base/common/lifecycle.js';
8
import { AuthenticationSession, AuthenticationSessionAccount, IAuthenticationProvider, IAuthenticationService, IAuthenticationExtensionsService } from '../../common/authentication.js';
9
import { IAuthenticationUsageService } from '../../browser/authenticationUsageService.js';
10
import { IAuthenticationMcpUsageService } from '../../browser/authenticationMcpUsageService.js';
11
import { IAuthenticationAccessService } from '../../browser/authenticationAccessService.js';
12
import { IAuthenticationMcpAccessService } from '../../browser/authenticationMcpAccessService.js';
13
import { IAuthenticationMcpService } from '../../browser/authenticationMcpService.js';
14
15
/**
16
* Helper function to create a mock authentication provider
17
*/
18
export function createProvider(overrides: Partial<IAuthenticationProvider> = {}): IAuthenticationProvider {
19
return {
20
id: 'test-provider',
21
label: 'Test Provider',
22
supportsMultipleAccounts: true,
23
createSession: () => Promise.resolve(createSession()),
24
removeSession: () => Promise.resolve(),
25
getSessions: () => Promise.resolve([]),
26
onDidChangeSessions: new Emitter<any>().event,
27
...overrides
28
};
29
}
30
31
/**
32
* Helper function to create a mock authentication session
33
*/
34
export function createSession(): AuthenticationSession {
35
return {
36
id: 'test-session',
37
accessToken: 'test-token',
38
account: { id: 'test-account', label: 'Test Account' },
39
scopes: ['read', 'write'],
40
idToken: undefined
41
};
42
}
43
44
/**
45
* Interface for tracking method calls in mock services
46
*/
47
interface MethodCall {
48
method: string;
49
args: any[];
50
timestamp: number;
51
}
52
53
/**
54
* Base class for test services with common functionality and call tracking
55
*/
56
export abstract class BaseTestService extends Disposable {
57
protected readonly data = new Map<string, any>();
58
private readonly _methodCalls: MethodCall[] = [];
59
60
protected getKey(...parts: string[]): string {
61
return parts.join('::');
62
}
63
64
/**
65
* Track a method call for verification in tests
66
*/
67
protected trackCall(method: string, ...args: any[]): void {
68
this._methodCalls.push({
69
method,
70
args: [...args],
71
timestamp: Date.now()
72
});
73
}
74
75
/**
76
* Get all method calls for verification
77
*/
78
getMethodCalls(): readonly MethodCall[] {
79
return [...this._methodCalls];
80
}
81
82
/**
83
* Get calls for a specific method
84
*/
85
getCallsFor(method: string): readonly MethodCall[] {
86
return this._methodCalls.filter(call => call.method === method);
87
}
88
89
/**
90
* Clear method call history
91
*/
92
clearCallHistory(): void {
93
this._methodCalls.length = 0;
94
}
95
96
/**
97
* Get the last call for a specific method
98
*/
99
getLastCallFor(method: string): MethodCall | undefined {
100
const calls = this.getCallsFor(method);
101
return calls[calls.length - 1];
102
}
103
}
104
105
/**
106
* Test implementation that actually stores and retrieves data
107
*/
108
export class TestUsageService extends BaseTestService implements IAuthenticationUsageService {
109
declare readonly _serviceBrand: undefined;
110
111
readAccountUsages(providerId: string, accountName: string): any[] {
112
this.trackCall('readAccountUsages', providerId, accountName);
113
return this.data.get(this.getKey(providerId, accountName)) || [];
114
}
115
116
addAccountUsage(providerId: string, accountName: string, scopes: readonly string[], extensionId: string, extensionName: string): void {
117
this.trackCall('addAccountUsage', providerId, accountName, scopes, extensionId, extensionName);
118
const key = this.getKey(providerId, accountName);
119
const usages = this.data.get(key) || [];
120
usages.push({ extensionId, extensionName, scopes: [...scopes], lastUsed: Date.now() });
121
this.data.set(key, usages);
122
}
123
124
removeAccountUsage(providerId: string, accountName: string): void {
125
this.trackCall('removeAccountUsage', providerId, accountName);
126
this.data.delete(this.getKey(providerId, accountName));
127
}
128
129
// Stub implementations for missing methods
130
async initializeExtensionUsageCache(): Promise<void> { }
131
async extensionUsesAuth(extensionId: string): Promise<boolean> { return false; }
132
}
133
134
export class TestMcpUsageService extends BaseTestService implements IAuthenticationMcpUsageService {
135
declare readonly _serviceBrand: undefined;
136
137
readAccountUsages(providerId: string, accountName: string): any[] {
138
this.trackCall('readAccountUsages', providerId, accountName);
139
return this.data.get(this.getKey(providerId, accountName)) || [];
140
}
141
142
addAccountUsage(providerId: string, accountName: string, scopes: readonly string[], mcpServerId: string, mcpServerName: string): void {
143
this.trackCall('addAccountUsage', providerId, accountName, scopes, mcpServerId, mcpServerName);
144
const key = this.getKey(providerId, accountName);
145
const usages = this.data.get(key) || [];
146
usages.push({ mcpServerId, mcpServerName, scopes: [...scopes], lastUsed: Date.now() });
147
this.data.set(key, usages);
148
}
149
150
removeAccountUsage(providerId: string, accountName: string): void {
151
this.trackCall('removeAccountUsage', providerId, accountName);
152
this.data.delete(this.getKey(providerId, accountName));
153
}
154
155
// Stub implementations for missing methods
156
async initializeUsageCache(): Promise<void> { }
157
async hasUsedAuth(mcpServerId: string): Promise<boolean> { return false; }
158
}
159
160
export class TestAccessService extends BaseTestService implements IAuthenticationAccessService {
161
declare readonly _serviceBrand: undefined;
162
private readonly _onDidChangeExtensionSessionAccess = this._register(new Emitter<any>());
163
onDidChangeExtensionSessionAccess = this._onDidChangeExtensionSessionAccess.event;
164
165
isAccessAllowed(providerId: string, accountName: string, extensionId: string): boolean | undefined {
166
this.trackCall('isAccessAllowed', providerId, accountName, extensionId);
167
const extensions = this.data.get(this.getKey(providerId, accountName)) || [];
168
const extension = extensions.find((e: any) => e.id === extensionId);
169
return extension?.allowed;
170
}
171
172
readAllowedExtensions(providerId: string, accountName: string): any[] {
173
this.trackCall('readAllowedExtensions', providerId, accountName);
174
return this.data.get(this.getKey(providerId, accountName)) || [];
175
}
176
177
updateAllowedExtensions(providerId: string, accountName: string, extensions: any[]): void {
178
this.trackCall('updateAllowedExtensions', providerId, accountName, extensions);
179
const key = this.getKey(providerId, accountName);
180
const existing = this.data.get(key) || [];
181
182
// Merge with existing data, updating or adding extensions
183
const merged = [...existing];
184
for (const ext of extensions) {
185
const existingIndex = merged.findIndex(e => e.id === ext.id);
186
if (existingIndex >= 0) {
187
merged[existingIndex] = ext;
188
} else {
189
merged.push(ext);
190
}
191
}
192
193
this.data.set(key, merged);
194
this._onDidChangeExtensionSessionAccess.fire({ providerId, accountName });
195
}
196
197
removeAllowedExtensions(providerId: string, accountName: string): void {
198
this.trackCall('removeAllowedExtensions', providerId, accountName);
199
this.data.delete(this.getKey(providerId, accountName));
200
}
201
}
202
203
export class TestMcpAccessService extends BaseTestService implements IAuthenticationMcpAccessService {
204
declare readonly _serviceBrand: undefined;
205
private readonly _onDidChangeMcpSessionAccess = this._register(new Emitter<any>());
206
onDidChangeMcpSessionAccess = this._onDidChangeMcpSessionAccess.event;
207
208
isAccessAllowed(providerId: string, accountName: string, mcpServerId: string): boolean | undefined {
209
this.trackCall('isAccessAllowed', providerId, accountName, mcpServerId);
210
const servers = this.data.get(this.getKey(providerId, accountName)) || [];
211
const server = servers.find((s: any) => s.id === mcpServerId);
212
return server?.allowed;
213
}
214
215
readAllowedMcpServers(providerId: string, accountName: string): any[] {
216
this.trackCall('readAllowedMcpServers', providerId, accountName);
217
return this.data.get(this.getKey(providerId, accountName)) || [];
218
}
219
220
updateAllowedMcpServers(providerId: string, accountName: string, mcpServers: any[]): void {
221
this.trackCall('updateAllowedMcpServers', providerId, accountName, mcpServers);
222
const key = this.getKey(providerId, accountName);
223
const existing = this.data.get(key) || [];
224
225
// Merge with existing data, updating or adding MCP servers
226
const merged = [...existing];
227
for (const server of mcpServers) {
228
const existingIndex = merged.findIndex(s => s.id === server.id);
229
if (existingIndex >= 0) {
230
merged[existingIndex] = server;
231
} else {
232
merged.push(server);
233
}
234
}
235
236
this.data.set(key, merged);
237
this._onDidChangeMcpSessionAccess.fire({ providerId, accountName });
238
}
239
240
removeAllowedMcpServers(providerId: string, accountName: string): void {
241
this.trackCall('removeAllowedMcpServers', providerId, accountName);
242
this.data.delete(this.getKey(providerId, accountName));
243
this._onDidChangeMcpSessionAccess.fire({ providerId, accountName });
244
}
245
}
246
247
export class TestPreferencesService extends BaseTestService {
248
private readonly _onDidChangeAccountPreference = this._register(new Emitter<any>());
249
onDidChangeAccountPreference = this._onDidChangeAccountPreference.event;
250
251
getAccountPreference(clientId: string, providerId: string): string | undefined {
252
return this.data.get(this.getKey(clientId, providerId));
253
}
254
255
updateAccountPreference(clientId: string, providerId: string, account: any): void {
256
this.data.set(this.getKey(clientId, providerId), account.label);
257
}
258
259
removeAccountPreference(clientId: string, providerId: string): void {
260
this.data.delete(this.getKey(clientId, providerId));
261
}
262
}
263
264
export class TestExtensionsService extends TestPreferencesService implements IAuthenticationExtensionsService {
265
declare readonly _serviceBrand: undefined;
266
267
// Stub implementations for methods we don't test
268
updateSessionPreference(): void { }
269
getSessionPreference(): string | undefined { return undefined; }
270
removeSessionPreference(): void { }
271
selectSession(): Promise<any> { return Promise.resolve(createSession()); }
272
requestSessionAccess(): void { }
273
requestNewSession(): Promise<void> { return Promise.resolve(); }
274
updateNewSessionRequests(): void { }
275
}
276
277
export class TestMcpService extends TestPreferencesService implements IAuthenticationMcpService {
278
declare readonly _serviceBrand: undefined;
279
280
// Stub implementations for methods we don't test
281
updateSessionPreference(): void { }
282
getSessionPreference(): string | undefined { return undefined; }
283
removeSessionPreference(): void { }
284
selectSession(): Promise<any> { return Promise.resolve(createSession()); }
285
requestSessionAccess(): void { }
286
requestNewSession(): Promise<void> { return Promise.resolve(); }
287
}
288
289
/**
290
* Minimal authentication service mock that only implements what we need
291
*/
292
export class TestAuthenticationService extends BaseTestService implements IAuthenticationService {
293
declare readonly _serviceBrand: undefined;
294
295
private readonly _onDidChangeSessions = this._register(new Emitter<any>());
296
private readonly _onDidRegisterAuthenticationProvider = this._register(new Emitter<any>());
297
private readonly _onDidUnregisterAuthenticationProvider = this._register(new Emitter<any>());
298
private readonly _onDidChangeDeclaredProviders = this._register(new Emitter<void>());
299
300
onDidChangeSessions = this._onDidChangeSessions.event;
301
onDidRegisterAuthenticationProvider = this._onDidRegisterAuthenticationProvider.event;
302
onDidUnregisterAuthenticationProvider = this._onDidUnregisterAuthenticationProvider.event;
303
onDidChangeDeclaredProviders = this._onDidChangeDeclaredProviders.event;
304
305
private readonly accountsMap = new Map<string, AuthenticationSessionAccount[]>();
306
307
registerAuthenticationProvider(id: string, provider: IAuthenticationProvider): void {
308
this.data.set(id, provider);
309
this._onDidRegisterAuthenticationProvider.fire({ id, label: provider.label });
310
}
311
312
getProviderIds(): string[] {
313
return Array.from(this.data.keys());
314
}
315
316
isAuthenticationProviderRegistered(id: string): boolean {
317
return this.data.has(id);
318
}
319
320
getProvider(id: string): IAuthenticationProvider {
321
return this.data.get(id)!;
322
}
323
324
addAccounts(providerId: string, accounts: AuthenticationSessionAccount[]): void {
325
this.accountsMap.set(providerId, accounts);
326
}
327
328
async getAccounts(providerId: string): Promise<readonly AuthenticationSessionAccount[]> {
329
return this.accountsMap.get(providerId) || [];
330
}
331
332
// All other methods are stubs since we don't test them
333
get declaredProviders(): any[] { return []; }
334
isDynamicAuthenticationProvider(): boolean { return false; }
335
async getSessions(): Promise<readonly AuthenticationSession[]> { return []; }
336
async createSession(): Promise<AuthenticationSession> { return createSession(); }
337
async removeSession(): Promise<void> { }
338
manageTrustedExtensionsForAccount(): void { }
339
async removeAccountSessions(): Promise<void> { }
340
registerDeclaredAuthenticationProvider(): void { }
341
unregisterDeclaredAuthenticationProvider(): void { }
342
unregisterAuthenticationProvider(): void { }
343
registerAuthenticationProviderHostDelegate(): IDisposable { return { dispose: () => { } }; }
344
createDynamicAuthenticationProvider(): Promise<any> { return Promise.resolve(undefined); }
345
async requestNewSession(): Promise<AuthenticationSession> { return createSession(); }
346
async getSession(): Promise<AuthenticationSession | undefined> { return createSession(); }
347
getOrActivateProviderIdForServer(): Promise<string | undefined> { return Promise.resolve(undefined); }
348
supportsHeimdallConnection(): boolean { return false; }
349
}
350
351