From 0b3111285a1143f1c28bf4ba41ba9aad199236f8 Mon Sep 17 00:00:00 2001 From: MicroFish91 <40250218+MicroFish91@users.noreply.github.com> Date: Mon, 10 Nov 2025 10:10:04 -0800 Subject: [PATCH 01/17] WIP --- .vscode/launch.json | 2 +- test/api/v4.test.ts | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 test/api/v4.test.ts diff --git a/.vscode/launch.json b/.vscode/launch.json index 1366f0de0..c758bf9f1 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -116,7 +116,7 @@ "MOCHA_timeout": "0", // Disable time-outs "DEBUGTELEMETRY": "v", "NODE_DEBUG": "", - "AzCode_EnableLongRunningTestsLocal": "" + "AzCode_EnableLongRunningTestsLocal": "true" } }, { diff --git a/test/api/v4.test.ts b/test/api/v4.test.ts new file mode 100644 index 000000000..2dba972a9 --- /dev/null +++ b/test/api/v4.test.ts @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import assert = require("assert"); +import { apiUtils } from "../../extension.bundle"; + +suite('v4 API tests', async () => { + test('v4 API should be defined', async () => { + const apiProvider = await apiUtils.getExtensionExports('ms-azuretools.vscode-azureresourcegroups'); + + assert.ok(apiProvider, 'API provider is undefined'); + + const v4Api = apiProvider.getApi('^4.0.0', { + extensionId: 'ms-azuretools.vscode-azureresourcegroups-tests', + }); + + assert.ok(v4Api); + }); +}); From f188ecad65071487cb8a96c825c1b640f5b947d2 Mon Sep 17 00:00:00 2001 From: MicroFish91 <40250218+MicroFish91@users.noreply.github.com> Date: Tue, 11 Nov 2025 15:46:17 -0800 Subject: [PATCH 02/17] Add auth tests and supporting logic --- .../AzExtUUIDCredentialManager.ts | 2 +- api/src/extensionApi.ts | 2 +- extension.bundle.ts | 6 +- src/api/auth/authApiInternal.ts | 3 +- .../createAzureResourcesAuthApiFactory.ts | 5 +- src/extension.ts | 6 +- test/api/auth/MockUUIDCredentialManager.ts | 13 +++ test/api/auth/mockApiProvider.ts | 21 ++++ test/api/auth/v4.test.ts | 104 ++++++++++++++++++ test/api/v4.test.ts | 21 ---- 10 files changed, 153 insertions(+), 30 deletions(-) create mode 100644 test/api/auth/MockUUIDCredentialManager.ts create mode 100644 test/api/auth/mockApiProvider.ts create mode 100644 test/api/auth/v4.test.ts delete mode 100644 test/api/v4.test.ts diff --git a/api/src/auth/credentialManager/AzExtUUIDCredentialManager.ts b/api/src/auth/credentialManager/AzExtUUIDCredentialManager.ts index b5c6eb67e..0ec0e01ff 100644 --- a/api/src/auth/credentialManager/AzExtUUIDCredentialManager.ts +++ b/api/src/auth/credentialManager/AzExtUUIDCredentialManager.ts @@ -11,7 +11,7 @@ import { AzExtCredentialManager } from "./AzExtCredentialManager"; * A simple, light-weight credential manager that issues and tracks randomly generated UUIDs for extension verification. */ export class AzExtUUIDCredentialManager implements AzExtCredentialManager { - private _uuidMap: Map = new Map(); + protected _uuidMap: Map = new Map(); createCredential(extensionId: string): string { const uuid: string = crypto.randomUUID(); diff --git a/api/src/extensionApi.ts b/api/src/extensionApi.ts index 04c0d8ce8..46c27bc02 100644 --- a/api/src/extensionApi.ts +++ b/api/src/extensionApi.ts @@ -16,7 +16,7 @@ export interface AzureResourcesExtensionApi extends AzureExtensionApi { /** * The authentication layer (v4) protecting the core Azure Resources extension API. */ -export interface AzureResourcesExtensionAuthApi extends AzureExtensionApi { +export interface AzureResourcesExtensionAuthApi extends Omit { getAzureResourcesApi(clientExtensionId: string, azureResourcesCredential: string, azureResourcesApiVersions: string[]): Promise<(AzureExtensionApi | undefined)[]>; createAzureResourcesApiSession(clientExtensionId: string, clientExtensionVersion: string, clientExtensionCredential: string): Promise; } diff --git a/extension.bundle.ts b/extension.bundle.ts index be2ac0deb..8b65b81b6 100644 --- a/extension.bundle.ts +++ b/extension.bundle.ts @@ -18,10 +18,12 @@ export { LocationListStep } from '@microsoft/vscode-azext-azureutils'; export * from '@microsoft/vscode-azext-utils'; export * from './api/src/AzExtResourceType'; // export * from './api/src'; -export * from './api/src/extensionApi'; +export * from './api/src/auth/credentialManager/AzExtCredentialManager'; +export * from './api/src/auth/credentialManager/AzExtUUIDCredentialManager'; export * from './api/src/resources/azure'; export * from './api/src/resources/base'; export * from './api/src/resources/workspace'; +export { apiUtils, AzureExtensionApi } from './api/src/utils/apiUtils'; export * from './api/src/utils/getApi'; export * from './api/src/utils/wrapper'; export { convertV1TreeItemId } from './src/api/compatibility/CompatibleAzExtTreeDataProvider'; @@ -34,6 +36,8 @@ export { createResourceGroup } from './src/commands/createResourceGroup'; export * from './src/commands/deleteResourceGroup/v2/deleteResourceGroupV2'; export { activate, deactivate } from './src/extension'; // Export for testing only - not part of public API +export * from './api/src/extensionApi'; +export * from './src/api/auth/createAzureResourcesAuthApiFactory'; export { AuthAccountStateManager, getAuthAccountStateManager } from './src/exportAuthRecord'; export * from './src/extensionVariables'; export * from './src/hostapi.v2.internal'; diff --git a/src/api/auth/authApiInternal.ts b/src/api/auth/authApiInternal.ts index 996e7e0db..91a1aa377 100644 --- a/src/api/auth/authApiInternal.ts +++ b/src/api/auth/authApiInternal.ts @@ -27,7 +27,8 @@ export async function createAzureResourcesApiSessionInternal(context: IActionCon try { const clientApi = await apiUtils.getAzureExtensionApi(ext.context, clientExtensionId, clientExtensionVersion); - await clientApi.receiveAzureResourcesApiSession?.(await credentialManager.createCredential(clientExtensionId), clientExtensionCredential); + const azureResourcesCredential: string = await credentialManager.createCredential(clientExtensionId); + await clientApi.receiveAzureResourcesApiSession?.(azureResourcesCredential, clientExtensionCredential); } catch (err) { const failed: string = localize('createResourcesApiSession.failed', 'Failed to create Azure Resources API session for extension "{0}".', clientExtensionId); ext.outputChannel.error(failed); diff --git a/src/api/auth/createAzureResourcesAuthApiFactory.ts b/src/api/auth/createAzureResourcesAuthApiFactory.ts index 8be4c7ef4..314516790 100644 --- a/src/api/auth/createAzureResourcesAuthApiFactory.ts +++ b/src/api/auth/createAzureResourcesAuthApiFactory.ts @@ -5,15 +5,12 @@ import { apiUtils, AzureExtensionApiFactory, callWithTelemetryAndErrorHandling, GetApiOptions, IActionContext } from '@microsoft/vscode-azext-utils'; import { AzExtCredentialManager } from '../../../api/src/auth/credentialManager/AzExtCredentialManager'; -import { AzExtUUIDCredentialManager } from '../../../api/src/auth/credentialManager/AzExtUUIDCredentialManager'; import { AzureResourcesAuthApiInternal } from '../../hostapi.v4.internal'; import { createAzureResourcesApiSessionInternal, getApiVerifyError, verifyAzureResourcesApiSessionInternal } from './authApiInternal'; const v4: string = '4.0.0'; -export function createAzureResourcesAuthApiFactory(coreApiProvider: apiUtils.AzureExtensionApiProvider): AzureExtensionApiFactory { - const credentialManager: AzExtCredentialManager = new AzExtUUIDCredentialManager(); - +export function createAzureResourcesAuthApiFactory(credentialManager: AzExtCredentialManager, coreApiProvider: apiUtils.AzureExtensionApiProvider): AzureExtensionApiFactory { return { apiVersion: v4, createApi: (options?: GetApiOptions) => { diff --git a/src/extension.ts b/src/extension.ts index 5794e139a..cbd681462 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -11,6 +11,8 @@ import { AzureSubscription } from 'api/src'; import { GetApiOptions, apiUtils } from 'api/src/utils/apiUtils'; import * as vscode from 'vscode'; import { AzExtResourceType } from '../api/src/AzExtResourceType'; +import { AzExtCredentialManager } from '../api/src/auth/credentialManager/AzExtCredentialManager'; +import { AzExtUUIDCredentialManager } from '../api/src/auth/credentialManager/AzExtUUIDCredentialManager'; import { DefaultAzureResourceProvider } from './api/DefaultAzureResourceProvider'; import { ResourceGroupsExtensionManager } from './api/ResourceGroupsExtensionManager'; import { ActivityLogResourceProviderManager, AzureResourceProviderManager, TenantResourceProviderManager, WorkspaceResourceProviderManager } from './api/ResourceProviderManagers'; @@ -264,6 +266,8 @@ export async function activate(context: vscode.ExtensionContext, perfStats: { lo v3ApiFactory, ]); + const credentialManager: AzExtCredentialManager = new AzExtUUIDCredentialManager(); + return createApiProvider( [ // Todo: Remove once extension clients finish migrating @@ -272,7 +276,7 @@ export async function activate(context: vscode.ExtensionContext, perfStats: { lo v3ApiFactory, // This will eventually be the only part of the API exposed publically - createAzureResourcesAuthApiFactory(coreApiProvider), + createAzureResourcesAuthApiFactory(credentialManager, coreApiProvider), ] ); } diff --git a/test/api/auth/MockUUIDCredentialManager.ts b/test/api/auth/MockUUIDCredentialManager.ts new file mode 100644 index 000000000..44a313669 --- /dev/null +++ b/test/api/auth/MockUUIDCredentialManager.ts @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import { AzExtUUIDCredentialManager } from "../../../extension.bundle"; + +export class MockUUIDCredentialManager extends AzExtUUIDCredentialManager { + // This value is normally protected, so we should add a getter for to monitor the values during tests + get uuidMap() { + return this._uuidMap; + } +} diff --git a/test/api/auth/mockApiProvider.ts b/test/api/auth/mockApiProvider.ts new file mode 100644 index 000000000..9ec380587 --- /dev/null +++ b/test/api/auth/mockApiProvider.ts @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import { apiUtils, AzureExtensionApi, AzureExtensionApiFactory, createApiProvider, GetApiOptions } from "../../../extension.bundle"; + +export function createMockApiProvider(versions: string[]): apiUtils.AzureExtensionApiProvider { + const apiFactories: AzureExtensionApiFactory[] = versions.map(version => { + return { + apiVersion: version, + createApi: (_options?: GetApiOptions) => { + return { + apiVersion: version, + }; + }, + }; + }); + + return createApiProvider(apiFactories); +} diff --git a/test/api/auth/v4.test.ts b/test/api/auth/v4.test.ts new file mode 100644 index 000000000..30508b9c3 --- /dev/null +++ b/test/api/auth/v4.test.ts @@ -0,0 +1,104 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import * as assert from "assert"; +import { apiUtils, AzExtCredentialManager, AzureResourcesExtensionAuthApi, createAzureResourcesAuthApiFactory, nonNullValue, parseError } from "../../../extension.bundle"; +import { assertThrowsAsync } from "../../wrapFunctionsInTelemetry.test"; +import { MockUUIDCredentialManager } from "./MockUUIDCredentialManager"; +import { createMockApiProvider } from "./mockApiProvider"; + +const extensionId: string = 'ms-azuretools.vscode-azureresourcegroups'; +const extensionVersion: string = '^4.0.0'; + +suite('v4 API auth layer tests', async () => { + test('v4 API should be defined', async () => { + const apiProvider = await apiUtils.getExtensionExports('ms-azuretools.vscode-azureresourcegroups'); + assert.ok(apiProvider, 'API provider is undefined'); + + const v4Api = apiProvider.getApi('^4.0.0', { extensionId: 'ms-azuretools.vscode-azureresourcegroups-tests' }); + assert.ok(v4Api); + }); + + test('createAzureResourcesApiSession should provide a credential but not return it directly', async () => { + const credentialManager = new MockUUIDCredentialManager(); + const coreApiVersions: string[] = ['0.0.1', '2.0.0', '3.0.0']; + const authApi: AzureResourcesExtensionAuthApi = createAuthApi(credentialManager, coreApiVersions); + + const apiSession = await authApi.createAzureResourcesApiSession(extensionId, extensionVersion, crypto.randomUUID()); + assert.equal(apiSession, undefined); + assert.ok(credentialManager.uuidMap.get(extensionId)); + }); + + test('createAzureResourcesApiSession should throw if an unallowed extension id is provided', async () => { + const credentialManager = new MockUUIDCredentialManager(); + const coreApiVersions: string[] = ['0.0.1', '2.0.0', '3.0.0']; + const authApi: AzureResourcesExtensionAuthApi = createAuthApi(credentialManager, coreApiVersions); + assertThrowsAsync(async () => await authApi.createAzureResourcesApiSession('extension1', extensionVersion, crypto.randomUUID())) + }); + + test('createAzureResourcesApiSession should not spill sensitive extension credentials in errors', async () => { + const credentialManager = new MockUUIDCredentialManager(); + credentialManager.createCredential('extension1'); + credentialManager.createCredential = () => { + throw new Error(credentialManager.uuidMap.get('extension1')); + } + + const coreApiVersions: string[] = ['0.0.1', '2.0.0', '3.0.0']; + const authApi: AzureResourcesExtensionAuthApi = createAuthApi(credentialManager, coreApiVersions); + + try { + await authApi.createAzureResourcesApiSession(extensionId, extensionVersion, crypto.randomUUID()); + assert.fail('We expect the credential manager to throw in this test.'); + } catch (err) { + const perr = parseError(err); + assert.doesNotMatch(perr.message, new RegExp(nonNullValue(credentialManager.uuidMap.get('extension1')), 'i')); + } + }); + + test('getAzureResourcesApis should return matching APIs if provided a valid credential', async () => { + const credentialManager = new MockUUIDCredentialManager(); + const coreApiVersions: string[] = ['0.0.1', '2.0.0', '3.0.0']; + + const authApi: AzureResourcesExtensionAuthApi = createAuthApi(credentialManager, coreApiVersions); + await authApi.createAzureResourcesApiSession(extensionId, extensionVersion, crypto.randomUUID()); + + const resourcesApis = await authApi.getAzureResourcesApi(extensionId, nonNullValue(credentialManager.uuidMap.get(extensionId)), ['0.0.1', '^2.0.0']); + assert.match(resourcesApis[0]?.apiVersion ?? '', /^0.0.1$/); + assert.match(resourcesApis[1]?.apiVersion ?? '', /^2./); + }); + + test('getAzureResourcesApis should throw if provided an invalid credential', async () => { + const credentialManager = new MockUUIDCredentialManager(); + const coreApiVersions: string[] = ['0.0.1', '2.0.0', '3.0.0']; + const authApi: AzureResourcesExtensionAuthApi = createAuthApi(credentialManager, coreApiVersions); + assertThrowsAsync(async () => await authApi.getAzureResourcesApi(extensionId, crypto.randomUUID(), ['^2.0.0'])); + }); + + test('getAzureResourcesApis should not spill sensitive extension credentials in errors', async () => { + const credentialManager = new MockUUIDCredentialManager(); + const coreApiVersions: string[] = ['0.0.1', '2.0.0', '3.0.0']; + const authApi: AzureResourcesExtensionAuthApi = createAuthApi(credentialManager, coreApiVersions); + + credentialManager.createCredential('extension1'); + credentialManager.createCredential('extension2'); + credentialManager.createCredential('extension3'); + + try { + await authApi.getAzureResourcesApi(extensionId, crypto.randomUUID(), ['^2.0.0']); + assert.fail('Should throw if requesting Azure Resources APIs without a valid credential.'); + } catch (err) { + const perr = parseError(err); + for (const credential of credentialManager.uuidMap.values()) { + assert.doesNotMatch(perr.message, new RegExp(credential, 'i')); + } + } + }); +}); + +function createAuthApi(credentialManager: AzExtCredentialManager, coreApiVersions: string[]): AzureResourcesExtensionAuthApi { + const coreApiProvider = createMockApiProvider(coreApiVersions); + const authApiProvider = createAzureResourcesAuthApiFactory(credentialManager, coreApiProvider); + return authApiProvider.createApi({ extensionId: 'ms-azuretools.vscode-azureresourcegroups-tests' }); +} diff --git a/test/api/v4.test.ts b/test/api/v4.test.ts deleted file mode 100644 index 2dba972a9..000000000 --- a/test/api/v4.test.ts +++ /dev/null @@ -1,21 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) Microsoft Corporation. All rights reserved. -* Licensed under the MIT License. See License.txt in the project root for license information. -*--------------------------------------------------------------------------------------------*/ - -import assert = require("assert"); -import { apiUtils } from "../../extension.bundle"; - -suite('v4 API tests', async () => { - test('v4 API should be defined', async () => { - const apiProvider = await apiUtils.getExtensionExports('ms-azuretools.vscode-azureresourcegroups'); - - assert.ok(apiProvider, 'API provider is undefined'); - - const v4Api = apiProvider.getApi('^4.0.0', { - extensionId: 'ms-azuretools.vscode-azureresourcegroups-tests', - }); - - assert.ok(v4Api); - }); -}); From e71785c4ba4f78afe138369bdbd1a33e117740d9 Mon Sep 17 00:00:00 2001 From: MicroFish91 <40250218+MicroFish91@users.noreply.github.com> Date: Tue, 11 Nov 2025 16:00:12 -0800 Subject: [PATCH 03/17] Update with better comments --- test/api/auth/MockUUIDCredentialManager.ts | 2 +- test/api/auth/mockApiProvider.ts | 4 ++++ test/api/auth/v4.test.ts | 5 ++++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/test/api/auth/MockUUIDCredentialManager.ts b/test/api/auth/MockUUIDCredentialManager.ts index 44a313669..5a7d87977 100644 --- a/test/api/auth/MockUUIDCredentialManager.ts +++ b/test/api/auth/MockUUIDCredentialManager.ts @@ -6,7 +6,7 @@ import { AzExtUUIDCredentialManager } from "../../../extension.bundle"; export class MockUUIDCredentialManager extends AzExtUUIDCredentialManager { - // This value is normally protected, so we should add a getter for to monitor the values during tests + // This value is normally protected, so we should add a getter so we have a way to monitor the values during tests get uuidMap() { return this._uuidMap; } diff --git a/test/api/auth/mockApiProvider.ts b/test/api/auth/mockApiProvider.ts index 9ec380587..a44142128 100644 --- a/test/api/auth/mockApiProvider.ts +++ b/test/api/auth/mockApiProvider.ts @@ -5,6 +5,10 @@ import { apiUtils, AzureExtensionApi, AzureExtensionApiFactory, createApiProvider, GetApiOptions } from "../../../extension.bundle"; +/** + * Creates a mock API provider with API factories matching the versions provided. + * Only the values required by the interface will be implemented. + */ export function createMockApiProvider(versions: string[]): apiUtils.AzureExtensionApiProvider { const apiFactories: AzureExtensionApiFactory[] = versions.map(version => { return { diff --git a/test/api/auth/v4.test.ts b/test/api/auth/v4.test.ts index 30508b9c3..ed33904fa 100644 --- a/test/api/auth/v4.test.ts +++ b/test/api/auth/v4.test.ts @@ -12,7 +12,7 @@ import { createMockApiProvider } from "./mockApiProvider"; const extensionId: string = 'ms-azuretools.vscode-azureresourcegroups'; const extensionVersion: string = '^4.0.0'; -suite('v4 API auth layer tests', async () => { +suite('v4 API auth tests', async () => { test('v4 API should be defined', async () => { const apiProvider = await apiUtils.getExtensionExports('ms-azuretools.vscode-azureresourcegroups'); assert.ok(apiProvider, 'API provider is undefined'); @@ -97,6 +97,9 @@ suite('v4 API auth layer tests', async () => { }); }); +/** + * Use to quickly bootstrap a testable auth API with core API factories matching the provided versions. + */ function createAuthApi(credentialManager: AzExtCredentialManager, coreApiVersions: string[]): AzureResourcesExtensionAuthApi { const coreApiProvider = createMockApiProvider(coreApiVersions); const authApiProvider = createAzureResourcesAuthApiFactory(credentialManager, coreApiProvider); From 3f4cb3ab89febfbba38a3db6ff89ed272f328d86 Mon Sep 17 00:00:00 2001 From: MicroFish91 <40250218+MicroFish91@users.noreply.github.com> Date: Tue, 11 Nov 2025 16:02:09 -0800 Subject: [PATCH 04/17] Revert launch change --- .vscode/launch.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index c758bf9f1..1366f0de0 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -116,7 +116,7 @@ "MOCHA_timeout": "0", // Disable time-outs "DEBUGTELEMETRY": "v", "NODE_DEBUG": "", - "AzCode_EnableLongRunningTestsLocal": "true" + "AzCode_EnableLongRunningTestsLocal": "" } }, { From 34d2f1ae64f2b9b37622e1c23709d23ff7ef14d8 Mon Sep 17 00:00:00 2001 From: MicroFish91 <40250218+MicroFish91@users.noreply.github.com> Date: Tue, 11 Nov 2025 16:15:23 -0800 Subject: [PATCH 05/17] Use extensionId var --- test/api/auth/v4.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/api/auth/v4.test.ts b/test/api/auth/v4.test.ts index ed33904fa..d170afe2c 100644 --- a/test/api/auth/v4.test.ts +++ b/test/api/auth/v4.test.ts @@ -14,7 +14,7 @@ const extensionVersion: string = '^4.0.0'; suite('v4 API auth tests', async () => { test('v4 API should be defined', async () => { - const apiProvider = await apiUtils.getExtensionExports('ms-azuretools.vscode-azureresourcegroups'); + const apiProvider = await apiUtils.getExtensionExports(extensionId); assert.ok(apiProvider, 'API provider is undefined'); const v4Api = apiProvider.getApi('^4.0.0', { extensionId: 'ms-azuretools.vscode-azureresourcegroups-tests' }); From c34d9521ebc1e231122372f4b64b1ade23176e6e Mon Sep 17 00:00:00 2001 From: MicroFish91 <40250218+MicroFish91@users.noreply.github.com> Date: Wed, 12 Nov 2025 10:54:30 -0800 Subject: [PATCH 06/17] Add note --- test/api/auth/v4.test.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/test/api/auth/v4.test.ts b/test/api/auth/v4.test.ts index d170afe2c..96f56a095 100644 --- a/test/api/auth/v4.test.ts +++ b/test/api/auth/v4.test.ts @@ -11,19 +11,25 @@ import { createMockApiProvider } from "./mockApiProvider"; const extensionId: string = 'ms-azuretools.vscode-azureresourcegroups'; const extensionVersion: string = '^4.0.0'; +const coreApiVersions: string[] = ['0.0.1', '2.0.0', '3.0.0']; suite('v4 API auth tests', async () => { test('v4 API should be defined', async () => { const apiProvider = await apiUtils.getExtensionExports(extensionId); assert.ok(apiProvider, 'API provider is undefined'); - const v4Api = apiProvider.getApi('^4.0.0', { extensionId: 'ms-azuretools.vscode-azureresourcegroups-tests' }); + const v4Api = apiProvider.getApi(extensionVersion, { extensionId: 'ms-azuretools.vscode-azureresourcegroups-tests' }); assert.ok(v4Api); }); + // NOTE: `createAzureResourcesApiSession` is not normally intended to be called directly by Azure Resources itself; however, I've found that it + // kind of still works for testing. It will basically run everything exactly the same except at the end - the exported API for Azure Resources will be missing + // the receiver method so the credential has no way to be passed back to the extension through its API. + // Since we inject and hold a copy of the credential manager during tests, we can simply grab the generated credential from the manager. + // Client side handshake testing should be done separately to ensure that the receiver method is being called and passed the correct credential. + test('createAzureResourcesApiSession should provide a credential but not return it directly', async () => { const credentialManager = new MockUUIDCredentialManager(); - const coreApiVersions: string[] = ['0.0.1', '2.0.0', '3.0.0']; const authApi: AzureResourcesExtensionAuthApi = createAuthApi(credentialManager, coreApiVersions); const apiSession = await authApi.createAzureResourcesApiSession(extensionId, extensionVersion, crypto.randomUUID()); @@ -33,7 +39,6 @@ suite('v4 API auth tests', async () => { test('createAzureResourcesApiSession should throw if an unallowed extension id is provided', async () => { const credentialManager = new MockUUIDCredentialManager(); - const coreApiVersions: string[] = ['0.0.1', '2.0.0', '3.0.0']; const authApi: AzureResourcesExtensionAuthApi = createAuthApi(credentialManager, coreApiVersions); assertThrowsAsync(async () => await authApi.createAzureResourcesApiSession('extension1', extensionVersion, crypto.randomUUID())) }); @@ -45,7 +50,6 @@ suite('v4 API auth tests', async () => { throw new Error(credentialManager.uuidMap.get('extension1')); } - const coreApiVersions: string[] = ['0.0.1', '2.0.0', '3.0.0']; const authApi: AzureResourcesExtensionAuthApi = createAuthApi(credentialManager, coreApiVersions); try { @@ -59,7 +63,6 @@ suite('v4 API auth tests', async () => { test('getAzureResourcesApis should return matching APIs if provided a valid credential', async () => { const credentialManager = new MockUUIDCredentialManager(); - const coreApiVersions: string[] = ['0.0.1', '2.0.0', '3.0.0']; const authApi: AzureResourcesExtensionAuthApi = createAuthApi(credentialManager, coreApiVersions); await authApi.createAzureResourcesApiSession(extensionId, extensionVersion, crypto.randomUUID()); @@ -78,7 +81,6 @@ suite('v4 API auth tests', async () => { test('getAzureResourcesApis should not spill sensitive extension credentials in errors', async () => { const credentialManager = new MockUUIDCredentialManager(); - const coreApiVersions: string[] = ['0.0.1', '2.0.0', '3.0.0']; const authApi: AzureResourcesExtensionAuthApi = createAuthApi(credentialManager, coreApiVersions); credentialManager.createCredential('extension1'); From 0b1d03eac3828a1bb0d62cc034ce34692b0d706c Mon Sep 17 00:00:00 2001 From: MicroFish91 <40250218+MicroFish91@users.noreply.github.com> Date: Wed, 12 Nov 2025 16:29:26 -0800 Subject: [PATCH 07/17] Update mock credential manager --- test/api/auth/MockUUIDCredentialManager.ts | 33 +++++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/test/api/auth/MockUUIDCredentialManager.ts b/test/api/auth/MockUUIDCredentialManager.ts index 5a7d87977..1c26e04e0 100644 --- a/test/api/auth/MockUUIDCredentialManager.ts +++ b/test/api/auth/MockUUIDCredentialManager.ts @@ -3,11 +3,36 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { AzExtUUIDCredentialManager } from "../../../extension.bundle"; +import { AzExtCredentialManager, maskValue } from "../../../extension.bundle"; + +/** + * A mock credential manager with the same implementation as `AzExtUUIDCredentialManager`, + * but with a public getter to inspect the UUIDs during test. + */ +export class MockUUIDCredentialManager implements AzExtCredentialManager { + #uuidMap: Map = new Map(); -export class MockUUIDCredentialManager extends AzExtUUIDCredentialManager { - // This value is normally protected, so we should add a getter so we have a way to monitor the values during tests get uuidMap() { - return this._uuidMap; + return this.#uuidMap; + } + + createCredential(extensionId: string): string { + const uuid: string = crypto.randomUUID(); + this.#uuidMap.set(extensionId, uuid); + return uuid; + } + + verifyCredential(credential: string, extensionId: string): boolean { + if (!credential || !extensionId) { + return false; + } + return credential === this.#uuidMap.get(extensionId); + } + + maskCredentials(data: string): string { + for (const uuid of this.#uuidMap.values()) { + data = maskValue(data, uuid); + } + return data; } } From 69051c074d411d87eb764a5bc2ac899525fb0681 Mon Sep 17 00:00:00 2001 From: MicroFish91 <40250218+MicroFish91@users.noreply.github.com> Date: Fri, 14 Nov 2025 15:20:11 -0800 Subject: [PATCH 08/17] Remove part of a comment --- test/api/auth/v4.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/test/api/auth/v4.test.ts b/test/api/auth/v4.test.ts index 29571801d..eb9163235 100644 --- a/test/api/auth/v4.test.ts +++ b/test/api/auth/v4.test.ts @@ -26,7 +26,6 @@ suite('v4 API auth tests', async () => { // kind of still works for testing. It will basically run everything exactly the same except at the end - the exported API for Azure Resources will be missing // the receiver method so the credential has no way to be passed back to the extension through its API. // Since we inject and hold a copy of the credential manager during tests, we can simply grab the generated credential from the manager. - // Client side handshake testing should be done separately to ensure that the receiver method is being called and passed the correct credential. test('createAzureResourcesApiSession should provide a credential but not return it directly', async () => { const credentialManager = new MockUUIDCredentialManager(); From 1718244e90c95d604d0a4c1862c6890067e8c5a8 Mon Sep 17 00:00:00 2001 From: MicroFish91 <40250218+MicroFish91@users.noreply.github.com> Date: Wed, 19 Nov 2025 10:52:15 -0800 Subject: [PATCH 09/17] Fix formatting --- test/api/auth/v4.host.test.ts | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/test/api/auth/v4.host.test.ts b/test/api/auth/v4.host.test.ts index 4bd3eb33c..cd8f04a2c 100644 --- a/test/api/auth/v4.host.test.ts +++ b/test/api/auth/v4.host.test.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ - import * as assert from "assert"; import { apiUtils, AzureExtensionApi, AzureResourcesExtensionAuthApi, nonNullValue, parseError } from "../../../extension.bundle"; import { assertThrowsAsync } from "../../wrapFunctionsInTelemetry.test"; @@ -18,26 +17,21 @@ suite('v4 internal API auth tests', async () => { const apiProvider = await apiUtils.getExtensionExports('ms-azuretools.vscode-azureresourcegroups'); assert.ok(apiProvider, 'API provider is undefined'); - const v4Api = apiProvider.getApi('^4.0.0', { extensionId: 'ms-azuretools.vscode-azureresourcegroups-tests' }); assert.ok(v4Api); }); - test('createAzureResourcesApiSession should provide a valid credential but not return it directly', async () => { let apiSession: unknown; let receivedHostCredential: string = ''; let receivedClientCredential: string = ''; - const credentialManager = new MockUUIDCredentialManager(); const generatedClientCredential: string = crypto.randomUUID(); - await new Promise((resolve) => { const timeout = setTimeout(resolve, 5000); - const mockClientExtensionApi: AzureExtensionApi = { apiVersion: clientExtensionVersion, receiveAzureResourcesApiSession: (hostCredential: string, clientCredential: string) => { @@ -48,29 +42,24 @@ suite('v4 internal API auth tests', async () => { }, } - const authApi: AzureResourcesExtensionAuthApi = createMockAuthApi({ credentialManager, clientApiProvider: { getApi: () => mockClientExtensionApi } }); authApi.createAzureResourcesApiSession(clientExtensionId, clientExtensionVersion, generatedClientCredential) .then(session => apiSession = session) .catch(() => { clearTimeout(timeout); resolve() }); }); - assert.equal(apiSession, undefined); assert.equal(receivedClientCredential, generatedClientCredential); - const generatedHostCredential: string = nonNullValue(credentialManager.uuidMap.get(clientExtensionId)); assert.equal(receivedHostCredential, generatedHostCredential); }); - test('createAzureResourcesApiSession should throw if an unallowed extension id is provided', async () => { const authApi: AzureResourcesExtensionAuthApi = createMockAuthApi(); assertThrowsAsync(async () => await authApi.createAzureResourcesApiSession('extension1', clientExtensionVersion, crypto.randomUUID())); }); - test('createAzureResourcesApiSession should not spill sensitive extension credentials in errors', async () => { const credentialManager = new MockUUIDCredentialManager(); credentialManager.createCredential('extension1'); @@ -78,10 +67,8 @@ suite('v4 internal API auth tests', async () => { throw new Error(credentialManager.uuidMap.get('extension1')); } - const authApi: AzureResourcesExtensionAuthApi = createMockAuthApi({ credentialManager }); - try { await authApi.createAzureResourcesApiSession(clientExtensionId, clientExtensionVersion, crypto.randomUUID()); assert.fail('We expect the credential manager to throw in this test.'); @@ -91,37 +78,30 @@ suite('v4 internal API auth tests', async () => { } }); - test('getAzureResourcesApis should return matching APIs if provided a valid credential', async () => { const credentialManager = new MockUUIDCredentialManager(); const generatedHostCredential: string = credentialManager.createCredential(clientExtensionId); - const authApi: AzureResourcesExtensionAuthApi = createMockAuthApi({ credentialManager }); const resourcesApis = await authApi.getAzureResourcesApis(clientExtensionId, generatedHostCredential, ['0.0.1', '^2.0.0']); - assert.match(resourcesApis[0]?.apiVersion ?? '', /^0.0.1$/); assert.match(resourcesApis[1]?.apiVersion ?? '', /^2./); }); - test('getAzureResourcesApis should throw if provided an invalid credential', async () => { const authApi: AzureResourcesExtensionAuthApi = createMockAuthApi(); assertThrowsAsync(async () => await authApi.getAzureResourcesApis(clientExtensionId, crypto.randomUUID(), ['^2.0.0'])); }); - test('getAzureResourcesApis should not spill sensitive extension credentials in errors', async () => { const credentialManager = new MockUUIDCredentialManager(); const authApi: AzureResourcesExtensionAuthApi = createMockAuthApi({ credentialManager }); - credentialManager.createCredential('extension1'); credentialManager.createCredential('extension2'); credentialManager.createCredential('extension3'); - try { await authApi.getAzureResourcesApis(clientExtensionId, crypto.randomUUID(), ['^2.0.0']); assert.fail('Should throw if requesting Azure Resources APIs without a valid credential.'); From ef7ad23d07015b391dc0e1b487a7c72502b15386 Mon Sep 17 00:00:00 2001 From: MicroFish91 <40250218+MicroFish91@users.noreply.github.com> Date: Wed, 19 Nov 2025 12:08:40 -0800 Subject: [PATCH 10/17] Match to equal --- test/api/auth/v4.host.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/api/auth/v4.host.test.ts b/test/api/auth/v4.host.test.ts index cd8f04a2c..34ea69281 100644 --- a/test/api/auth/v4.host.test.ts +++ b/test/api/auth/v4.host.test.ts @@ -85,7 +85,7 @@ suite('v4 internal API auth tests', async () => { const authApi: AzureResourcesExtensionAuthApi = createMockAuthApi({ credentialManager }); const resourcesApis = await authApi.getAzureResourcesApis(clientExtensionId, generatedHostCredential, ['0.0.1', '^2.0.0']); - assert.match(resourcesApis[0]?.apiVersion ?? '', /^0.0.1$/); + assert.equal(resourcesApis[0]?.apiVersion, '0.0.1'); assert.match(resourcesApis[1]?.apiVersion ?? '', /^2./); }); From eda6ce6bbcf464e86096af9a979c8e15d59ebb76 Mon Sep 17 00:00:00 2001 From: MicroFish91 <40250218+MicroFish91@users.noreply.github.com> Date: Wed, 19 Nov 2025 13:02:29 -0800 Subject: [PATCH 11/17] Escape the symbol --- test/api/auth/v4.host.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/api/auth/v4.host.test.ts b/test/api/auth/v4.host.test.ts index 34ea69281..63530391b 100644 --- a/test/api/auth/v4.host.test.ts +++ b/test/api/auth/v4.host.test.ts @@ -86,7 +86,7 @@ suite('v4 internal API auth tests', async () => { const resourcesApis = await authApi.getAzureResourcesApis(clientExtensionId, generatedHostCredential, ['0.0.1', '^2.0.0']); assert.equal(resourcesApis[0]?.apiVersion, '0.0.1'); - assert.match(resourcesApis[1]?.apiVersion ?? '', /^2./); + assert.match(resourcesApis[1]?.apiVersion ?? '', /^2\./); }); test('getAzureResourcesApis should throw if provided an invalid credential', async () => { From 5c11b97b1cc544e0121940ab8691aceff36b1dda Mon Sep 17 00:00:00 2001 From: MicroFish91 <40250218+MicroFish91@users.noreply.github.com> Date: Thu, 20 Nov 2025 13:55:35 -0800 Subject: [PATCH 12/17] Inject more extension vars --- .vscode/launch.json | 3 ++- src/extension.ts | 4 ++++ src/testApi.ts | 7 ++++++- test/global.test.ts | 5 +++-- test/utils/testApiAccess.ts | 2 +- 5 files changed, 16 insertions(+), 5 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index bbc0d91b1..7f1df4543 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -69,7 +69,8 @@ "preLaunchTask": "Watch: ESBuild", "env": { "DEBUGTELEMETRY": "v", - "AzCode_EnableLongRunningTestsLocal": "" + "AzCode_EnableLongRunningTestsLocal": "", + "VSCODE_RUNNING_TESTS": "true", } }, ] diff --git a/src/extension.ts b/src/extension.ts index 03556a94f..3f8bffc6c 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -264,6 +264,10 @@ export async function activate(context: vscode.ExtensionContext, perfStats: { lo apiVersion: '99.0.0', createApi: () => ({ apiVersion: '99.0.0', + extVars: { + context: ext.context, + outputChannel: ext.outputChannel, + }, getApi: () => ext.v2.api, compatibility: { getAppResourceTree: () => ext.appResourceTree, diff --git a/src/testApi.ts b/src/testApi.ts index 0ac517080..904dd641d 100644 --- a/src/testApi.ts +++ b/src/testApi.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { AzureSubscriptionProvider } from "@microsoft/vscode-azext-azureauth"; -import { AzExtTreeDataProvider } from "@microsoft/vscode-azext-utils"; +import { AzExtTreeDataProvider, UIExtensionVariables } from "@microsoft/vscode-azext-utils"; import { AzureResourcesApiInternal } from "./hostapi.v2.internal"; import { AzureResourcesServiceFactory } from "./services/AzureResourcesService"; @@ -19,6 +19,11 @@ export interface TestApi { */ apiVersion: '99.0.0'; + /** + * UI extension variables often used for registration in AzExt shared packages + */ + extVars: UIExtensionVariables; + /** * Get the extension's internal API instance */ diff --git a/test/global.test.ts b/test/global.test.ts index 7d66ce046..e16515f6b 100644 --- a/test/global.test.ts +++ b/test/global.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { registerOnActionStartHandler, TestUserInput } from '@microsoft/vscode-azext-utils'; +import { registerOnActionStartHandler, registerUIExtensionVariables, TestUserInput } from '@microsoft/vscode-azext-utils'; import * as vscode from 'vscode'; import { settingUtils } from '../src/utils/settingUtils'; import { getTestApi } from './utils/testApiAccess'; @@ -19,7 +19,8 @@ suiteSetup(async function (this: Mocha.Context): Promise { this.timeout(1 * 60 * 1000); // Initialize test API - this caches it for use throughout tests - await getTestApi(); + const testApi = await getTestApi(); + registerUIExtensionVariables(testApi.extVars); await vscode.commands.executeCommand('azureResourceGroups.refresh'); // activate the extension before tests begin diff --git a/test/utils/testApiAccess.ts b/test/utils/testApiAccess.ts index 1811606c1..12fdb7d93 100644 --- a/test/utils/testApiAccess.ts +++ b/test/utils/testApiAccess.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { TestApi } from '../../src/testApi'; import { apiUtils } from '../../api/src/utils/apiUtils'; +import { TestApi } from '../../src/testApi'; let cachedTestApi: TestApi | undefined; From 8edafe9436c1c840f5c25fce2ae72a877a388399 Mon Sep 17 00:00:00 2001 From: MicroFish91 <40250218+MicroFish91@users.noreply.github.com> Date: Fri, 21 Nov 2025 09:00:00 -0800 Subject: [PATCH 13/17] Add type --- test/global.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/global.test.ts b/test/global.test.ts index adba6ea5a..6348022f0 100644 --- a/test/global.test.ts +++ b/test/global.test.ts @@ -5,6 +5,7 @@ import { registerOnActionStartHandler, registerUIExtensionVariables, TestUserInput } from '@microsoft/vscode-azext-utils'; import * as vscode from 'vscode'; +import { TestApi } from '../src/testApi'; import { settingUtils } from '../src/utils/settingUtils'; import { getTestApi } from './utils/testApiAccess'; @@ -19,7 +20,7 @@ suiteSetup(async function (this: Mocha.Context): Promise { this.timeout(1 * 60 * 1000); // Initialize test API - this caches it for use throughout tests - const testApi = await getTestApi(); + const testApi: TestApi = await getTestApi(); registerUIExtensionVariables(testApi.extensionVariables.getUI()); await vscode.commands.executeCommand('azureResourceGroups.refresh'); // activate the extension before tests begin From 0d63c1cd5ebc711857b987fd22216ada507d77e0 Mon Sep 17 00:00:00 2001 From: MicroFish91 <40250218+MicroFish91@users.noreply.github.com> Date: Fri, 21 Nov 2025 09:15:24 -0800 Subject: [PATCH 14/17] Add an extra test --- test/api/auth/v4.host.test.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/test/api/auth/v4.host.test.ts b/test/api/auth/v4.host.test.ts index a73c1fe95..557c13509 100644 --- a/test/api/auth/v4.host.test.ts +++ b/test/api/auth/v4.host.test.ts @@ -41,12 +41,12 @@ suite('v4 internal API auth tests', async () => { receivedClientCredential = clientCredential; resolve(); }, - } + }; const authApi: AzureResourcesExtensionAuthApi = createMockAuthApi({ credentialManager, clientApiProvider: { getApi: () => mockClientExtensionApi } }); authApi.createAzureResourcesApiSession(clientExtensionId, clientExtensionVersion, generatedClientCredential) .then(session => apiSession = session) - .catch(() => { clearTimeout(timeout); resolve() }); + .catch(() => { clearTimeout(timeout); resolve(); }); }); assert.equal(apiSession, undefined); @@ -58,7 +58,7 @@ suite('v4 internal API auth tests', async () => { test('createAzureResourcesApiSession should throw if an unallowed extension id is provided', async () => { const authApi: AzureResourcesExtensionAuthApi = createMockAuthApi(); - assertThrowsAsync(async () => await authApi.createAzureResourcesApiSession('extension1', clientExtensionVersion, crypto.randomUUID())); + await assertThrowsAsync(async () => await authApi.createAzureResourcesApiSession('extension1', clientExtensionVersion, crypto.randomUUID())); }); test('createAzureResourcesApiSession should not spill sensitive extension credentials in errors', async () => { @@ -66,7 +66,7 @@ suite('v4 internal API auth tests', async () => { credentialManager.createCredential('extension1'); credentialManager.createCredential = () => { throw new Error(credentialManager.uuidMap.get('extension1')); - } + }; const authApi: AzureResourcesExtensionAuthApi = createMockAuthApi({ credentialManager }); @@ -91,8 +91,12 @@ suite('v4 internal API auth tests', async () => { }); test('getAzureResourcesApis should throw if provided an invalid credential', async () => { - const authApi: AzureResourcesExtensionAuthApi = createMockAuthApi(); - assertThrowsAsync(async () => await authApi.getAzureResourcesApis(clientExtensionId, crypto.randomUUID(), ['^2.0.0'])); + const credentialManager = new MockUUIDCredentialManager(); + const authApi: AzureResourcesExtensionAuthApi = createMockAuthApi({ credentialManager }); + await assertThrowsAsync(async () => await authApi.getAzureResourcesApis(clientExtensionId, crypto.randomUUID(), ['^2.0.0'])); + + credentialManager.createCredential(clientExtensionId); + await assertThrowsAsync(async () => await authApi.getAzureResourcesApis(clientExtensionId, crypto.randomUUID(), ['^2.0.0'])); }); test('getAzureResourcesApis should not spill sensitive extension credentials in errors', async () => { From 56385e1ce67076fffac988e11e506953b200f33f Mon Sep 17 00:00:00 2001 From: MicroFish91 <40250218+MicroFish91@users.noreply.github.com> Date: Mon, 8 Dec 2025 19:32:13 -0800 Subject: [PATCH 15/17] Remove env var --- .vscode/launch.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 7f1df4543..bbc0d91b1 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -69,8 +69,7 @@ "preLaunchTask": "Watch: ESBuild", "env": { "DEBUGTELEMETRY": "v", - "AzCode_EnableLongRunningTestsLocal": "", - "VSCODE_RUNNING_TESTS": "true", + "AzCode_EnableLongRunningTestsLocal": "" } }, ] From 1039782fde59706c3994e6f82dd27c10eb78a726 Mon Sep 17 00:00:00 2001 From: MicroFish91 <40250218+MicroFish91@users.noreply.github.com> Date: Thu, 11 Dec 2025 11:23:25 -0800 Subject: [PATCH 16/17] Upgrade utils package --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 037ba7659..1fcd84ab2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@azure/arm-resources-profile-2020-09-01-hybrid": "^2.1.0", "@microsoft/vscode-azext-azureauth": "^5.1.1", "@microsoft/vscode-azext-azureutils": "^4.0.0", - "@microsoft/vscode-azext-utils": "^4.0.2", + "@microsoft/vscode-azext-utils": "^4.0.3", "form-data": "^4.0.4", "fs-extra": "^11.3.0", "jsonc-parser": "^2.2.1", @@ -1650,9 +1650,9 @@ } }, "node_modules/@microsoft/vscode-azext-utils": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@microsoft/vscode-azext-utils/-/vscode-azext-utils-4.0.2.tgz", - "integrity": "sha512-RpHKn4hcDTtJpMaif0jb8ulfDEFPT/C/U3wcx93eLxgQhDVFQ5aC49KybxodNp89dVUXhNUHhu6gdNDPfXt6Pg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@microsoft/vscode-azext-utils/-/vscode-azext-utils-4.0.3.tgz", + "integrity": "sha512-Qli06TAOPqW91cRtHI17jIW344ew487FF+YydZ4pM0o0zomi9VR9yJjZ+1vVnhHmTZuFTUCrI6w+I8JByWxM5w==", "license": "MIT", "dependencies": { "@microsoft/vscode-azureresources-api": "^3.0.0", diff --git a/package.json b/package.json index dd2d36340..b415da487 100644 --- a/package.json +++ b/package.json @@ -923,7 +923,7 @@ "@azure/arm-resources-profile-2020-09-01-hybrid": "^2.1.0", "@microsoft/vscode-azext-azureauth": "^5.1.1", "@microsoft/vscode-azext-azureutils": "^4.0.0", - "@microsoft/vscode-azext-utils": "^4.0.2", + "@microsoft/vscode-azext-utils": "^4.0.3", "form-data": "^4.0.4", "fs-extra": "^11.3.0", "jsonc-parser": "^2.2.1", From 0aa6f1c8fa8d1496b888cdd4d8ebf1c4a9f4e370 Mon Sep 17 00:00:00 2001 From: MicroFish91 <40250218+MicroFish91@users.noreply.github.com> Date: Thu, 11 Dec 2025 11:24:45 -0800 Subject: [PATCH 17/17] Remove ui extension vars; leverage testGlobalSetup --- src/extension.ts | 6 ------ src/testApi.ts | 7 +------ test/global.test.ts | 7 +++---- 3 files changed, 4 insertions(+), 16 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index ef6991bd8..ba07c283e 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -269,12 +269,6 @@ export async function activate(context: vscode.ExtensionContext, perfStats: { lo getAppResourceTree: () => ext.appResourceTree, }, extensionVariables: { - getUI: () => { - return { - context: ext.context, - outputChannel: ext.outputChannel, - }; - }, getOutputChannel: () => ext.outputChannel, }, testing: { diff --git a/src/testApi.ts b/src/testApi.ts index 3815f71dd..328308724 100644 --- a/src/testApi.ts +++ b/src/testApi.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { AzureSubscriptionProvider } from "@microsoft/vscode-azext-azureauth"; -import { AzExtTreeDataProvider, IAzExtLogOutputChannel, UIExtensionVariables } from "@microsoft/vscode-azext-utils"; +import { AzExtTreeDataProvider, IAzExtLogOutputChannel } from "@microsoft/vscode-azext-utils"; import { AzureResourcesApiInternal } from "./hostapi.v2.internal"; import { AzureResourcesServiceFactory } from "./services/AzureResourcesService"; @@ -38,11 +38,6 @@ export interface TestApi { * Get extension variables for tests */ extensionVariables: { - /** - * Get the common extension variables used throughout the UI package - */ - getUI(): UIExtensionVariables; - /** * Get the output channel */ diff --git a/test/global.test.ts b/test/global.test.ts index 6348022f0..991ace0f2 100644 --- a/test/global.test.ts +++ b/test/global.test.ts @@ -3,9 +3,8 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { registerOnActionStartHandler, registerUIExtensionVariables, TestUserInput } from '@microsoft/vscode-azext-utils'; +import { registerOnActionStartHandler, testGlobalSetup, TestUserInput } from '@microsoft/vscode-azext-utils'; import * as vscode from 'vscode'; -import { TestApi } from '../src/testApi'; import { settingUtils } from '../src/utils/settingUtils'; import { getTestApi } from './utils/testApiAccess'; @@ -20,8 +19,8 @@ suiteSetup(async function (this: Mocha.Context): Promise { this.timeout(1 * 60 * 1000); // Initialize test API - this caches it for use throughout tests - const testApi: TestApi = await getTestApi(); - registerUIExtensionVariables(testApi.extensionVariables.getUI()); + await getTestApi(); + testGlobalSetup(); await vscode.commands.executeCommand('azureResourceGroups.refresh'); // activate the extension before tests begin