diff --git a/src/utils/wrapFunctionsInTelemetry.ts b/src/utils/wrapFunctionsInTelemetry.ts index ac31c2e4..1000e06e 100644 --- a/src/utils/wrapFunctionsInTelemetry.ts +++ b/src/utils/wrapFunctionsInTelemetry.ts @@ -16,7 +16,7 @@ function stringifyError(e: unknown): string { } function handleError(e: unknown, functionName: string): never { - ext.outputChannel.appendLog(`Internal error: '${functionName}' threw an exception\n\t${stringifyError(e)}`); + ext.outputChannel?.appendLog(`Internal error: '${functionName}' threw an exception\n\t${stringifyError(e)}`); if (e instanceof Error) { e.message = functionName === 'branchDataProvider.getResourceItem' ? // shortened message for anything displayed on the tree diff --git a/test/nightly/crud.test.ts b/test/nightly/crud.test.ts index 47caf502..747f1bd0 100644 --- a/test/nightly/crud.test.ts +++ b/test/nightly/crud.test.ts @@ -6,11 +6,15 @@ import type { Location } from '@azure/arm-resources-subscriptions'; import { AzExtParentTreeItem, createTestActionContext, randomUtils, runWithTestActionContext } from '@microsoft/vscode-azext-utils'; import assert from "assert"; +import * as vscode from 'vscode'; import { SubscriptionItem } from '../../src/tree/azure/SubscriptionItem'; import { settingUtils } from '../../src/utils/settingUtils'; import { longRunningTestsEnabled } from "../global.test"; +import { setupAzureDevOpsSubscriptionProvider } from "../utils/azureDevOpsSubscriptionProvider"; import { getCachedTestApi } from "../utils/testApiAccess"; +const useAzureFederatedCredentials: boolean = !/^(false|0)?$/i.test(process.env['AzCode_UseAzureFederatedCredentials'] || ''); + let rgName: string; let locations: Location[]; let testSubscription: SubscriptionItem; @@ -24,21 +28,46 @@ suite('Resource CRUD Operations', function (this: Mocha.Suite): void { } const testApi = getCachedTestApi(); + // Clear mock overrides that may have been set by other test suites testApi.testing.setOverrideAzureServiceFactory(undefined); testApi.testing.setOverrideAzureSubscriptionProvider(undefined); + // Re-establish the AzDO federated credential provider if running in a pipeline, + // since other test suites may have overwritten it with a mock provider. + if (useAzureFederatedCredentials) { + await setupAzureDevOpsSubscriptionProvider(); + } + + // Refresh the tree and wait for any pending tree operations to settle. + // This avoids a race condition where a background tree refresh (triggered by + // the logIn command in global.nightly.test.ts) cancels our getChildren() call + // via the shared cancellation token in AzureResourceTreeDataProvider. + await vscode.commands.executeCommand('azureResourceGroups.refresh'); + await new Promise(resolve => setTimeout(resolve, 2000)); + const subscriptionTreeItems = await testApi.compatibility.getAppResourceTree().getChildren() as unknown as SubscriptionItem[]; - if (subscriptionTreeItems.length > 0) { - const testContext = await createTestActionContext(); - testSubscription = subscriptionTreeItems[0] as SubscriptionItem; - const context = { - ...testContext, - ...testSubscription.subscription, - environment: structuredClone(testSubscription.subscription.environment) - }; - locations = (await testApi.testing.getLocations(context)).slice(0, 5); // limit to 5 locations for test speed + + // Filter to actual SubscriptionItems (exclude sign-in/placeholder items). + const actualSubscriptions = subscriptionTreeItems.filter( + (item): item is SubscriptionItem => !!(item as SubscriptionItem).subscription?.subscriptionId + ); + + // if we can't find any subscriptions, then something is wrong with the test setup (e.g. auth failure), so skip the tests rather than fail them + if (actualSubscriptions.length === 0) { + this.skip(); + return; } + const testContext = await createTestActionContext(); + testSubscription = actualSubscriptions[0]; + + const context = { + ...testContext, + ...testSubscription.subscription, + environment: structuredClone(testSubscription.subscription.environment) + }; + locations = (await testApi.testing.getLocations(context)).slice(0, 5); // limit to 5 locations for test speed + rgName = randomUtils.getRandomHexString(12); }); diff --git a/test/utils/azureDevOpsSubscriptionProvider.ts b/test/utils/azureDevOpsSubscriptionProvider.ts index a73ac7e8..d15ec83c 100644 --- a/test/utils/azureDevOpsSubscriptionProvider.ts +++ b/test/utils/azureDevOpsSubscriptionProvider.ts @@ -43,6 +43,14 @@ export async function setupAzureDevOpsSubscriptionProvider(): Promise { // Create the provider instance now so we can return it synchronously const provider = await factory(); + // Sign in to establish the token credential. + // This must be done before the provider can return subscriptions. + const signedIn = await provider.signIn(); + + if (!signedIn) { + throw new Error('Failed to sign in with Azure DevOps federated credentials'); + } + // Set the override via the test API const testApi = getCachedTestApi(); testApi.testing.setOverrideAzureSubscriptionProvider(() => provider);