diff --git a/src/commands/image/imageSource/containerRegistry/acr/AcrListStep.ts b/src/commands/image/imageSource/containerRegistry/acr/AcrListStep.ts index 1bb092f46..44087b0f5 100644 --- a/src/commands/image/imageSource/containerRegistry/acr/AcrListStep.ts +++ b/src/commands/image/imageSource/containerRegistry/acr/AcrListStep.ts @@ -16,6 +16,7 @@ import { RegistryNameStep } from "./createAcr/RegistryNameStep"; import { SkuListStep } from "./createAcr/SkuListStep"; export interface AcrListStepOptions { + createIfNone?: boolean; suppressCreatePick?: boolean; pickUpdateStrategy?: AcrPickUpdateStrategy; } @@ -48,7 +49,7 @@ export class AcrListStep extends let result: Registry | typeof noMatchingResources | undefined; do { const picks: IAzureQuickPickItem[] = await this.getPicks(context); - if (picks.length === 1 && picks[0] === acrCreatePick) { + if (this.options.createIfNone && picks.length === 1 && picks[0] === acrCreatePick) { pick = acrCreatePick; result = pick.data as typeof acrCreatePick['data']; break; diff --git a/test/nightly/deployWorkspaceProject/deployWorkspaceProject.test.ts b/test/nightly/deployWorkspaceProject/deployWorkspaceProject.test.ts index 02b9dc35d..7836c31e8 100644 --- a/test/nightly/deployWorkspaceProject/deployWorkspaceProject.test.ts +++ b/test/nightly/deployWorkspaceProject/deployWorkspaceProject.test.ts @@ -3,18 +3,11 @@ * Licensed under the MIT License. See LICENSE.md in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { type ManagedEnvironment } from "@azure/arm-appcontainers"; -import { type Registry } from "@azure/arm-containerregistry"; -import { runWithTestActionContext } from "@microsoft/vscode-azext-dev"; -import { nonNullProp, randomUtils } from "@microsoft/vscode-azext-utils"; -import * as assert from "assert"; -import { createAcr, createManagedEnvironment } from "../../../extension.bundle"; +import { nonNullProp } from "@microsoft/vscode-azext-utils"; import { longRunningTestsEnabled } from '../../global.test'; -import { resourceGroupsToDelete } from "../global.nightly.test"; -import { buildParallelTestScenarios, type DwpParallelTestScenario } from './buildParallelScenarios'; +import { generateParallelTests, type DwpParallelTestScenario } from "./parallelTests"; -let setupTask: Promise; -const testScenarios: DwpParallelTestScenario[] = buildParallelTestScenarios(); +const testScenarios: DwpParallelTestScenario[] = generateParallelTests(); suite('deployWorkspaceProject', async function (this: Mocha.Suite) { this.timeout(15 * 60 * 1000); @@ -24,13 +17,8 @@ suite('deployWorkspaceProject', async function (this: Mocha.Suite) { this.skip(); } - // Create a container registry & managed environment first so that we can guarantee one is always built before workspace deployment tests start. - // This is crucial for test consistency because some resource prompts will skip if no existing resources exist to choose from - // Creating at least one of each resource first ensures consistent reproduceability. - setupTask = setupResources(); - for (const s of testScenarios) { - s.scenario = s.callback(setupTask); + s.scenario = s.callback(); } }); @@ -40,39 +28,3 @@ suite('deployWorkspaceProject', async function (this: Mocha.Suite) { }); } }); - -async function setupResources(): Promise { - let envResourceTask: Promise | undefined; - let managedEnvironment: ManagedEnvironment | undefined; - try { - envResourceTask = runWithTestActionContext('createManagedEnvironment', async context => { - const resourceName: string = 'dwp' + randomUtils.getRandomHexString(6); - await context.ui.runWithInputs([resourceName, 'East US'], async () => { - managedEnvironment = await createManagedEnvironment(context); - }); - }); - } catch (e) { - console.error(e); - } - - let acrResourceTask: Promise | undefined; - let registry: Registry | undefined; - try { - acrResourceTask = runWithTestActionContext('createContainerRegistry', async context => { - const resourceName: string = 'dwp' + randomUtils.getRandomHexString(6); - await context.ui.runWithInputs([resourceName, 'Basic', 'East US'], async () => { - registry = await createAcr(context); - }); - }); - } catch (e) { - console.error(e); - } - - await Promise.allSettled([envResourceTask, acrResourceTask]); - - assert.ok(managedEnvironment, 'Failed to create managed environment - skipping "deployWorkspaceProject" tests.'); - resourceGroupsToDelete.add(nonNullProp(managedEnvironment, 'name')); - - assert.ok(registry, 'Failed to create container registry - skipping "deployWorkspaceProject" tests.'); - resourceGroupsToDelete.add(nonNullProp(registry, 'name')); -} diff --git a/test/nightly/deployWorkspaceProject/dwpTestScenarios.ts b/test/nightly/deployWorkspaceProject/dwpTestScenarios.ts deleted file mode 100644 index 8f631e3ff..000000000 --- a/test/nightly/deployWorkspaceProject/dwpTestScenarios.ts +++ /dev/null @@ -1,44 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See LICENSE.md in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { longRunningLocalTestsEnabled } from "../../global.test"; -import { generateAlbumApiJavaScriptTestCases } from "./testCases/albumApiJavaScriptTestCases"; -import { type DeployWorkspaceProjectTestCase } from "./testCases/DeployWorkspaceProjectTestCase"; -import { generateMonoRepoAdminCredentialsTestCases } from "./testCases/monoRepoTestCases/adminCredentialsTestCases"; -import { generateMonoRepoIdentityTestCases } from "./testCases/monoRepoTestCases/identityTestCases"; - -export interface DeployWorkspaceProjectTestScenario { - label: string; - folderName: string; - testCases: DeployWorkspaceProjectTestCase[]; -} - -export function getDwpTestScenarios(): DeployWorkspaceProjectTestScenario[] { - const dwpTestScenarios: DeployWorkspaceProjectTestScenario[] = [ - { - label: 'albumapi-js', - folderName: 'albumapi-js', - testCases: generateAlbumApiJavaScriptTestCases(), - }, - { - label: 'monorepo-admincreds', - folderName: 'monorepo-admincreds', - testCases: generateMonoRepoAdminCredentialsTestCases(), - }, - ]; - - if (longRunningLocalTestsEnabled) { - // Insufficient auth privilege to test managed identity / role assignment in our manual testing subscription. - // Therefore, limit these tests to only run locally in personal subscriptions where user has full permission to assign roles. - // Todo: Investigate if it makes sense to elevate remote privileges such that these tests can also be automated to run remotely. - dwpTestScenarios.push({ - label: 'monorepo-identity', - folderName: 'monorepo-identity', - testCases: generateMonoRepoIdentityTestCases(), - }); - } - - return dwpTestScenarios; -} diff --git a/test/nightly/deployWorkspaceProject/dwpTestUtils.ts b/test/nightly/deployWorkspaceProject/dwpTestUtils.ts index 86d264465..5ed4dc208 100644 --- a/test/nightly/deployWorkspaceProject/dwpTestUtils.ts +++ b/test/nightly/deployWorkspaceProject/dwpTestUtils.ts @@ -10,7 +10,7 @@ import * as assert from "assert"; import { createContainerAppsAPIClient, type DeployWorkspaceProjectResults } from "../../../extension.bundle"; import { type StringOrRegExpProps } from "../../typeUtils"; import { subscriptionContext } from "../global.nightly.test"; -import { type PostTestAssertion } from "./testCases/DeployWorkspaceProjectTestCase"; +import { type PostTestAssertion } from "./scenarios/DeployWorkspaceProjectTestScenario"; export namespace dwpTestUtils { export function generateExpectedResultsWithCredentials(sharedResourceName: string, acrResourceName: string, appResourceName: string): StringOrRegExpProps { @@ -19,10 +19,10 @@ export namespace dwpTestUtils { imageName: new RegExp(appResourceName, 'i'), logAnalyticsWorkspaceId: new RegExp(`\/resourceGroups\/${sharedResourceName}\/providers\/Microsoft\.OperationalInsights\/workspaces\/${sharedResourceName}`, 'i'), managedEnvironmentId: new RegExp(`\/resourceGroups\/${sharedResourceName}\/providers\/Microsoft\.App\/managedEnvironments\/${sharedResourceName}`, 'i'), - registryId: new RegExp(`\/resourceGroups\/${sharedResourceName}\/providers\/Microsoft\.ContainerRegistry\/registries\/${acrResourceName}.{6}`, 'i'), - registryLoginServer: new RegExp(`${acrResourceName}.{6}\.azurecr\.io`, 'i'), + registryId: new RegExp(`\/resourceGroups\/${sharedResourceName}\/providers\/Microsoft\.ContainerRegistry\/registries\/${acrResourceName}`, 'i'), + registryLoginServer: new RegExp(`${acrResourceName}(?:.{6})?\.azurecr\.io`, 'i'), registryPassword: new RegExp('.*'), - registryUsername: new RegExp(`${acrResourceName}.{6}`, 'i'), + registryUsername: new RegExp(acrResourceName, 'i'), resourceGroupId: new RegExp(`\/resourceGroups\/${sharedResourceName}`, 'i') }; } @@ -33,8 +33,8 @@ export namespace dwpTestUtils { imageName: new RegExp(appResourceName, 'i'), logAnalyticsWorkspaceId: new RegExp(`\/resourceGroups\/${sharedResourceName}\/providers\/Microsoft\.OperationalInsights\/workspaces\/${sharedResourceName}`, 'i'), managedEnvironmentId: new RegExp(`\/resourceGroups\/${sharedResourceName}\/providers\/Microsoft\.App\/managedEnvironments\/${sharedResourceName}`, 'i'), - registryId: new RegExp(`\/resourceGroups\/${sharedResourceName}\/providers\/Microsoft\.ContainerRegistry\/registries\/${acrResourceName}.{6}`, 'i'), - registryLoginServer: new RegExp(`${acrResourceName}.{6}\.azurecr\.io`, 'i'), + registryId: new RegExp(`\/resourceGroups\/${sharedResourceName}\/providers\/Microsoft\.ContainerRegistry\/registries\/${acrResourceName}`, 'i'), + registryLoginServer: new RegExp(`${acrResourceName}(?:.{6})?\.azurecr\.io`, 'i'), registryPassword: undefined, registryUsername: undefined, resourceGroupId: new RegExp(`\/resourceGroups\/${sharedResourceName}`, 'i') diff --git a/test/nightly/deployWorkspaceProject/buildParallelScenarios.ts b/test/nightly/deployWorkspaceProject/parallelTests.ts similarity index 89% rename from test/nightly/deployWorkspaceProject/buildParallelScenarios.ts rename to test/nightly/deployWorkspaceProject/parallelTests.ts index 87b9ac5ef..eafaf849f 100644 --- a/test/nightly/deployWorkspaceProject/buildParallelScenarios.ts +++ b/test/nightly/deployWorkspaceProject/parallelTests.ts @@ -10,27 +10,26 @@ import { workspace, type Uri, type WorkspaceFolder } from "vscode"; import { AzExtFsExtra, deployWorkspaceProject, dwpSettingUtilsV2, ext, parseError, settingUtils, type DeploymentConfigurationSettings, type DeployWorkspaceProjectResults, type IParsedError } from "../../../extension.bundle"; import { assertStringPropsMatch, getWorkspaceFolderUri } from "../../testUtils"; import { resourceGroupsToDelete } from "../global.nightly.test"; -import { getDwpTestScenarios, type DeployWorkspaceProjectTestScenario } from "./dwpTestScenarios"; +import { type DeployWorkspaceProjectTestScenario } from "./scenarios/DeployWorkspaceProjectTestScenario"; +import { generateTestScenarios } from "./scenarios/testScenarios"; export interface DwpParallelTestScenario { title: string; - callback(setupTask: Promise): Promise; + callback(): Promise; scenario?: Promise; } -export function buildParallelTestScenarios(): DwpParallelTestScenario[] { - return getDwpTestScenarios().map(scenario => { +export function generateParallelTests(): DwpParallelTestScenario[] { + return generateTestScenarios().map(scenario => { return { title: scenario.label, - callback: buildParallelScenarioCallback(scenario), + callback: runTestScenario(scenario), }; }); } -function buildParallelScenarioCallback(scenario: DeployWorkspaceProjectTestScenario): DwpParallelTestScenario['callback'] { - return async (setupTask: Promise) => { - await setupTask; - +function runTestScenario(scenario: DeployWorkspaceProjectTestScenario): DwpParallelTestScenario['callback'] { + return async () => { const workspaceFolderUri: Uri = getWorkspaceFolderUri(scenario.folderName); const rootFolder: WorkspaceFolder | undefined = workspace.getWorkspaceFolder(workspaceFolderUri); assert.ok(rootFolder, 'Could not retrieve root workspace folder.'); diff --git a/test/nightly/deployWorkspaceProject/testCases/DeployWorkspaceProjectTestCase.ts b/test/nightly/deployWorkspaceProject/scenarios/DeployWorkspaceProjectTestScenario.ts similarity index 92% rename from test/nightly/deployWorkspaceProject/testCases/DeployWorkspaceProjectTestCase.ts rename to test/nightly/deployWorkspaceProject/scenarios/DeployWorkspaceProjectTestScenario.ts index 3fb11b9b9..57e7fa49d 100644 --- a/test/nightly/deployWorkspaceProject/testCases/DeployWorkspaceProjectTestCase.ts +++ b/test/nightly/deployWorkspaceProject/scenarios/DeployWorkspaceProjectTestScenario.ts @@ -6,6 +6,12 @@ import { type DeploymentConfigurationSettings, type DeployWorkspaceProjectResults, type IActionContext } from "../../../../extension.bundle"; import { type StringOrRegExpProps } from "../../../typeUtils"; +export interface DeployWorkspaceProjectTestScenario { + label: string; + folderName: string; + testCases: DeployWorkspaceProjectTestCase[]; +} + export interface DeployWorkspaceProjectTestCase { /** * Label to display when executing the test diff --git a/test/nightly/deployWorkspaceProject/testCases/monoRepoTestCases/adminCredentialsTestCases.ts b/test/nightly/deployWorkspaceProject/scenarios/monorepo/adminCredentialsScenario.ts similarity index 54% rename from test/nightly/deployWorkspaceProject/testCases/monoRepoTestCases/adminCredentialsTestCases.ts rename to test/nightly/deployWorkspaceProject/scenarios/monorepo/adminCredentialsScenario.ts index 0ff67f602..1bb30c54b 100644 --- a/test/nightly/deployWorkspaceProject/testCases/monoRepoTestCases/adminCredentialsTestCases.ts +++ b/test/nightly/deployWorkspaceProject/scenarios/monorepo/adminCredentialsScenario.ts @@ -5,37 +5,49 @@ import { randomUtils } from "@microsoft/vscode-azext-utils"; import * as path from "path"; +import { type DeploymentConfigurationSettings } from "../../../../../extension.bundle"; +import { type StringOrRegExpProps } from "../../../../typeUtils"; import { dwpTestUtils } from "../../dwpTestUtils"; -import { type DeployWorkspaceProjectTestCase } from "../DeployWorkspaceProjectTestCase"; -import { generateExpectedDeploymentConfiguration } from "./generateExpectedDeploymentConfiguration"; +import { type DeployWorkspaceProjectTestCase } from "../DeployWorkspaceProjectTestScenario"; -export function generateMonoRepoAdminCredentialsTestCases(): DeployWorkspaceProjectTestCase[] { +export function generateMonorepoAdminCredentialsTests(): DeployWorkspaceProjectTestCase[] { const folderName: string = 'monorepo-admincreds'; const sharedResourceName: string = 'monorepo-ac' + randomUtils.getRandomHexString(4); const acrResourceName: string = sharedResourceName.replace(/[^a-zA-Z0-9]+/g, ''); + const appOneName: string = 'monorepo-ac-app1'; + const appTwoName: string = 'monorepo-ac-app2'; + const appThreeName: string = 'monorepo-ac-app3'; + return [ { label: "Deploy App 1", inputs: [ new RegExp(folderName, 'i'), - path.join('app1', 'Dockerfile'), + 'Advanced', new RegExp('Create new container apps environment', 'i'), - new RegExp('Create new container registry', 'i'), - 'Continue', sharedResourceName, - 'app1', - `.${path.sep}app1`, + new RegExp('Create new resource group', 'i'), + sharedResourceName, + new RegExp('Create new container registry', 'i'), + acrResourceName, + 'Standard', + new RegExp('Create new container app'), + appOneName, + 'East US', 'Docker Login Credentials', 'Enable', + path.join('app1', 'Dockerfile'), + `.${path.sep}app1`, + `${appOneName}:latest`, path.join('app1', '.env.example'), - 'East US', + 'Continue', 'Save' ], - expectedResults: dwpTestUtils.generateExpectedResultsWithCredentials(sharedResourceName, acrResourceName, 'app1'), + expectedResults: dwpTestUtils.generateExpectedResultsWithCredentials(sharedResourceName, acrResourceName, appOneName), expectedVSCodeSettings: { deploymentConfigurations: [ - generateExpectedDeploymentConfiguration(sharedResourceName, acrResourceName, 'app1') + generateExpectedMonorepoDeploymentConfiguration(sharedResourceName, acrResourceName, appOneName, 'app1') ] }, postTestAssertion: dwpTestUtils.generatePostTestAssertion({ targetPort: 3000, env: [{ name: 'MESSAGE', value: 'container apps (app1)' }] }), @@ -46,21 +58,24 @@ export function generateMonoRepoAdminCredentialsTestCases(): DeployWorkspaceProj inputs: [ new RegExp(folderName, 'i'), new RegExp('Create and deploy new app configuration', 'i'), + 'Advanced', + sharedResourceName, + acrResourceName, + new RegExp('Create new container app'), + appTwoName, + 'Docker Login Credentials', path.join('app2', 'Dockerfile'), - new RegExp('(Recommended)', 'i'), // Select a container app environment - new RegExp(sharedResourceName, 'i'), // Select a container registry - this should match the description of the ACR previously created in the same resource group - 'Continue', - 'app2', `.${path.sep}app2`, - 'Docker Login Credentials', + `${appTwoName}:latest`, path.join('app2', '.env.example'), + 'Continue', 'Save' ], - expectedResults: dwpTestUtils.generateExpectedResultsWithCredentials(sharedResourceName, acrResourceName, 'app2'), + expectedResults: dwpTestUtils.generateExpectedResultsWithCredentials(sharedResourceName, acrResourceName, appTwoName), expectedVSCodeSettings: { deploymentConfigurations: [ - generateExpectedDeploymentConfiguration(sharedResourceName, acrResourceName, 'app1'), - generateExpectedDeploymentConfiguration(sharedResourceName, acrResourceName, 'app2'), + generateExpectedMonorepoDeploymentConfiguration(sharedResourceName, acrResourceName, appOneName, 'app1'), + generateExpectedMonorepoDeploymentConfiguration(sharedResourceName, acrResourceName, appTwoName, 'app2'), ] }, postTestAssertion: dwpTestUtils.generatePostTestAssertion({ targetPort: 3001, env: [{ name: 'MESSAGE', value: 'container apps (app2)' }] }) @@ -70,22 +85,25 @@ export function generateMonoRepoAdminCredentialsTestCases(): DeployWorkspaceProj inputs: [ new RegExp(folderName, 'i'), new RegExp('Create and deploy new app configuration', 'i'), + 'Advanced', + sharedResourceName, + acrResourceName, + new RegExp('Create new container app'), + appThreeName, + 'Docker Login Credentials', path.join('app3', 'Dockerfile'), - new RegExp('(Recommended)', 'i'), // Select a container app environment - new RegExp(sharedResourceName, 'i'), // Select a container registry - this should match the description of the ACR previously created in the same resource group - 'Continue', - 'app3', `.${path.sep}app3`, - 'Docker Login Credentials', + `${appThreeName}:latest`, path.join('app3', '.env.example'), + 'Continue', 'Save' ], - expectedResults: dwpTestUtils.generateExpectedResultsWithCredentials(sharedResourceName, acrResourceName, 'app3'), + expectedResults: dwpTestUtils.generateExpectedResultsWithCredentials(sharedResourceName, acrResourceName, appThreeName), expectedVSCodeSettings: { deploymentConfigurations: [ - generateExpectedDeploymentConfiguration(sharedResourceName, acrResourceName, 'app1'), - generateExpectedDeploymentConfiguration(sharedResourceName, acrResourceName, 'app2'), - generateExpectedDeploymentConfiguration(sharedResourceName, acrResourceName, 'app3'), + generateExpectedMonorepoDeploymentConfiguration(sharedResourceName, acrResourceName, appOneName, 'app1'), + generateExpectedMonorepoDeploymentConfiguration(sharedResourceName, acrResourceName, appTwoName, 'app2'), + generateExpectedMonorepoDeploymentConfiguration(sharedResourceName, acrResourceName, appThreeName, 'app3'), ] }, postTestAssertion: dwpTestUtils.generatePostTestAssertion({ targetPort: 3002, env: [{ name: 'MESSAGE', value: 'container apps (app3)' }] }), @@ -94,18 +112,31 @@ export function generateMonoRepoAdminCredentialsTestCases(): DeployWorkspaceProj label: "Re-deploy App 1", inputs: [ new RegExp(folderName, 'i'), - 'app1', + appOneName, 'Continue' ], - expectedResults: dwpTestUtils.generateExpectedResultsWithCredentials(sharedResourceName, acrResourceName, 'app1'), + expectedResults: dwpTestUtils.generateExpectedResultsWithCredentials(sharedResourceName, acrResourceName, appOneName), expectedVSCodeSettings: { deploymentConfigurations: [ - generateExpectedDeploymentConfiguration(sharedResourceName, acrResourceName, 'app1'), - generateExpectedDeploymentConfiguration(sharedResourceName, acrResourceName, 'app2'), - generateExpectedDeploymentConfiguration(sharedResourceName, acrResourceName, 'app3'), + generateExpectedMonorepoDeploymentConfiguration(sharedResourceName, acrResourceName, appOneName, 'app1'), + generateExpectedMonorepoDeploymentConfiguration(sharedResourceName, acrResourceName, appTwoName, 'app2'), + generateExpectedMonorepoDeploymentConfiguration(sharedResourceName, acrResourceName, appThreeName, 'app3'), ] }, postTestAssertion: dwpTestUtils.generatePostTestAssertion({ targetPort: 3000, env: [{ name: 'MESSAGE', value: 'container apps (app1)' }] }) } ]; } + +export function generateExpectedMonorepoDeploymentConfiguration(sharedResourceName: string, acrResourceName: string, appResourceName: string, rootFolder: string): StringOrRegExpProps { + return { + label: appResourceName, + type: 'AcrDockerBuildRequest', + dockerfilePath: path.join(rootFolder, 'Dockerfile'), + srcPath: rootFolder, + envPath: path.join(rootFolder, '.env.example'), + resourceGroup: sharedResourceName, + containerApp: appResourceName, + containerRegistry: new RegExp(acrResourceName, 'i'), + }; +} diff --git a/test/nightly/deployWorkspaceProject/testCases/monoRepoTestCases/identityTestCases.ts b/test/nightly/deployWorkspaceProject/scenarios/monorepo/identityScenario.ts similarity index 59% rename from test/nightly/deployWorkspaceProject/testCases/monoRepoTestCases/identityTestCases.ts rename to test/nightly/deployWorkspaceProject/scenarios/monorepo/identityScenario.ts index 837dd8d3c..f28a0a03d 100644 --- a/test/nightly/deployWorkspaceProject/testCases/monoRepoTestCases/identityTestCases.ts +++ b/test/nightly/deployWorkspaceProject/scenarios/monorepo/identityScenario.ts @@ -6,35 +6,46 @@ import { randomUtils } from "@microsoft/vscode-azext-utils"; import * as path from "path"; import { dwpTestUtils } from "../../dwpTestUtils"; -import { type DeployWorkspaceProjectTestCase } from "../DeployWorkspaceProjectTestCase"; -import { generateExpectedDeploymentConfiguration } from "./generateExpectedDeploymentConfiguration"; +import { type DeployWorkspaceProjectTestCase } from "../DeployWorkspaceProjectTestScenario"; +import { generateExpectedMonorepoDeploymentConfiguration } from "./adminCredentialsScenario"; -export function generateMonoRepoIdentityTestCases(): DeployWorkspaceProjectTestCase[] { +export function generateMonorepoIdentityTests(): DeployWorkspaceProjectTestCase[] { const folderName: string = 'monorepo-identity'; const sharedResourceName: string = 'monorepo-id' + randomUtils.getRandomHexString(4); const acrResourceName: string = sharedResourceName.replace(/[^a-zA-Z0-9]+/g, ''); + const appOneName: string = 'monorepo-id-app1'; + const appTwoName: string = 'monorepo-id-app2'; + const appThreeName: string = 'monorepo-id-app3'; + return [ { label: "Deploy App 1", inputs: [ new RegExp(folderName, 'i'), - path.join('app1', 'Dockerfile'), + 'Advanced', new RegExp('Create new container apps environment', 'i'), - new RegExp('Create new container registry', 'i'), - 'Continue', sharedResourceName, - 'app1', - `.${path.sep}app1`, + new RegExp('Create new resource group', 'i'), + sharedResourceName, + new RegExp('Create new container registry', 'i'), + acrResourceName, + 'Standard', + new RegExp('Create new container app'), + appOneName, + 'East US', 'Managed Identity', + path.join('app1', 'Dockerfile'), + `.${path.sep}app1`, + `${appOneName}:latest`, path.join('app1', '.env.example'), - 'East US', + 'Continue', 'Save' ], - expectedResults: dwpTestUtils.generateExpectedResultsWithoutCredentials(sharedResourceName, acrResourceName, 'app1'), + expectedResults: dwpTestUtils.generateExpectedResultsWithoutCredentials(sharedResourceName, acrResourceName, appOneName), expectedVSCodeSettings: { deploymentConfigurations: [ - generateExpectedDeploymentConfiguration(sharedResourceName, acrResourceName, 'app1') + generateExpectedMonorepoDeploymentConfiguration(sharedResourceName, acrResourceName, appOneName, 'app1') ] }, postTestAssertion: dwpTestUtils.generatePostTestAssertion({ targetPort: 3000, env: [{ name: 'MESSAGE', value: 'container apps (app1)' }] }), @@ -45,21 +56,24 @@ export function generateMonoRepoIdentityTestCases(): DeployWorkspaceProjectTestC inputs: [ new RegExp(folderName, 'i'), new RegExp('Create and deploy new app configuration', 'i'), + 'Advanced', + sharedResourceName, + acrResourceName, + new RegExp('Create new container app'), + appTwoName, + 'Managed Identity', path.join('app2', 'Dockerfile'), - new RegExp('(Recommended)', 'i'), // Select a container app environment - new RegExp(sharedResourceName, 'i'), // Select a container registry - this should match the description of the ACR previously created in the same resource group - 'Continue', - 'app2', `.${path.sep}app2`, - 'Managed Identity', + `${appTwoName}:latest`, path.join('app2', '.env.example'), + 'Continue', 'Save' ], - expectedResults: dwpTestUtils.generateExpectedResultsWithoutCredentials(sharedResourceName, acrResourceName, 'app2'), + expectedResults: dwpTestUtils.generateExpectedResultsWithoutCredentials(sharedResourceName, acrResourceName, appTwoName), expectedVSCodeSettings: { deploymentConfigurations: [ - generateExpectedDeploymentConfiguration(sharedResourceName, acrResourceName, 'app1'), - generateExpectedDeploymentConfiguration(sharedResourceName, acrResourceName, 'app2'), + generateExpectedMonorepoDeploymentConfiguration(sharedResourceName, acrResourceName, appOneName, 'app1'), + generateExpectedMonorepoDeploymentConfiguration(sharedResourceName, acrResourceName, appTwoName, 'app2'), ] }, postTestAssertion: dwpTestUtils.generatePostTestAssertion({ targetPort: 3001, env: [{ name: 'MESSAGE', value: 'container apps (app2)' }] }) @@ -69,22 +83,25 @@ export function generateMonoRepoIdentityTestCases(): DeployWorkspaceProjectTestC inputs: [ new RegExp(folderName, 'i'), new RegExp('Create and deploy new app configuration', 'i'), + 'Advanced', + sharedResourceName, + acrResourceName, + new RegExp('Create new container app'), + appThreeName, + 'Managed Identity', path.join('app3', 'Dockerfile'), - new RegExp('(Recommended)', 'i'), // Select a container app environment - new RegExp(sharedResourceName, 'i'), // Select a container registry - this should match the description of the ACR previously created in the same resource group - 'Continue', - 'app3', `.${path.sep}app3`, - 'Managed Identity', + `${appThreeName}:latest`, path.join('app3', '.env.example'), + 'Continue', 'Save' ], - expectedResults: dwpTestUtils.generateExpectedResultsWithoutCredentials(sharedResourceName, acrResourceName, 'app3'), + expectedResults: dwpTestUtils.generateExpectedResultsWithoutCredentials(sharedResourceName, acrResourceName, appThreeName), expectedVSCodeSettings: { deploymentConfigurations: [ - generateExpectedDeploymentConfiguration(sharedResourceName, acrResourceName, 'app1'), - generateExpectedDeploymentConfiguration(sharedResourceName, acrResourceName, 'app2'), - generateExpectedDeploymentConfiguration(sharedResourceName, acrResourceName, 'app3'), + generateExpectedMonorepoDeploymentConfiguration(sharedResourceName, acrResourceName, appOneName, 'app1'), + generateExpectedMonorepoDeploymentConfiguration(sharedResourceName, acrResourceName, appTwoName, 'app2'), + generateExpectedMonorepoDeploymentConfiguration(sharedResourceName, acrResourceName, appThreeName, 'app3'), ] }, postTestAssertion: dwpTestUtils.generatePostTestAssertion({ targetPort: 3002, env: [{ name: 'MESSAGE', value: 'container apps (app3)' }] }), @@ -93,15 +110,15 @@ export function generateMonoRepoIdentityTestCases(): DeployWorkspaceProjectTestC label: "Re-deploy App 1", inputs: [ new RegExp(folderName, 'i'), - 'app1', + appOneName, 'Continue' ], - expectedResults: dwpTestUtils.generateExpectedResultsWithoutCredentials(sharedResourceName, acrResourceName, 'app1'), + expectedResults: dwpTestUtils.generateExpectedResultsWithoutCredentials(sharedResourceName, acrResourceName, appOneName), expectedVSCodeSettings: { deploymentConfigurations: [ - generateExpectedDeploymentConfiguration(sharedResourceName, acrResourceName, 'app1'), - generateExpectedDeploymentConfiguration(sharedResourceName, acrResourceName, 'app2'), - generateExpectedDeploymentConfiguration(sharedResourceName, acrResourceName, 'app3'), + generateExpectedMonorepoDeploymentConfiguration(sharedResourceName, acrResourceName, appOneName, 'app1'), + generateExpectedMonorepoDeploymentConfiguration(sharedResourceName, acrResourceName, appTwoName, 'app2'), + generateExpectedMonorepoDeploymentConfiguration(sharedResourceName, acrResourceName, appThreeName, 'app3'), ] }, postTestAssertion: dwpTestUtils.generatePostTestAssertion({ targetPort: 3000, env: [{ name: 'MESSAGE', value: 'container apps (app1)' }] }) diff --git a/test/nightly/deployWorkspaceProject/scenarios/polyrepo/advancedJSScenario.ts b/test/nightly/deployWorkspaceProject/scenarios/polyrepo/advancedJSScenario.ts new file mode 100644 index 000000000..a6aefdc12 --- /dev/null +++ b/test/nightly/deployWorkspaceProject/scenarios/polyrepo/advancedJSScenario.ts @@ -0,0 +1,67 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See LICENSE.md in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { randomUtils } from "@microsoft/vscode-azext-utils"; +import * as path from "path"; +import { dwpTestUtils } from "../../dwpTestUtils"; +import { type DeployWorkspaceProjectTestCase } from "../DeployWorkspaceProjectTestScenario"; +import { generateExpectedJSDeploymentConfiguration } from "./basicJSScenario"; + +export function generateAdvancedJSTests(): DeployWorkspaceProjectTestCase[] { + const folderName: string = 'advanced-js'; + const appResourceName: string = 'a-js-app'; + const sharedResourceName: string = 'a-js-env' + randomUtils.getRandomHexString(4); + const acrResourceName: string = sharedResourceName.replace(/[^a-zA-Z0-9]+/g, ''); + + return [ + { + label: 'Deploy App', + inputs: [ + new RegExp(folderName, 'i'), + 'Advanced', + new RegExp('Create new container apps environment', 'i'), + sharedResourceName, + new RegExp('Create new resource group', 'i'), + sharedResourceName, + new RegExp('Create new container registry', 'i'), + acrResourceName, + 'Standard', + new RegExp('Create new container app'), + appResourceName, + 'East US', + 'Docker Login Credentials', + 'Enable', + path.join('src', 'Dockerfile'), + `.${path.sep}src`, + `${appResourceName}:latest`, + 'Continue', + 'Save' + ], + expectedResults: dwpTestUtils.generateExpectedResultsWithCredentials(sharedResourceName, acrResourceName, appResourceName), + expectedVSCodeSettings: { + deploymentConfigurations: [ + generateExpectedJSDeploymentConfiguration(sharedResourceName, acrResourceName, appResourceName) + ] + }, + postTestAssertion: dwpTestUtils.generatePostTestAssertion({ targetPort: 8080, env: undefined }), + resourceGroupToDelete: sharedResourceName + }, + { + label: 'Re-deploy App', + inputs: [ + new RegExp(folderName, 'i'), + appResourceName, + 'Continue' + ], + expectedResults: dwpTestUtils.generateExpectedResultsWithCredentials(sharedResourceName, acrResourceName, appResourceName), + expectedVSCodeSettings: { + deploymentConfigurations: [ + generateExpectedJSDeploymentConfiguration(sharedResourceName, acrResourceName, appResourceName) + ] + }, + postTestAssertion: dwpTestUtils.generatePostTestAssertion({ targetPort: 8080, env: undefined }) + } + ]; +} diff --git a/test/nightly/deployWorkspaceProject/testCases/albumApiJavaScriptTestCases.ts b/test/nightly/deployWorkspaceProject/scenarios/polyrepo/basicJSScenario.ts similarity index 68% rename from test/nightly/deployWorkspaceProject/testCases/albumApiJavaScriptTestCases.ts rename to test/nightly/deployWorkspaceProject/scenarios/polyrepo/basicJSScenario.ts index 3d598cd15..80a351221 100644 --- a/test/nightly/deployWorkspaceProject/testCases/albumApiJavaScriptTestCases.ts +++ b/test/nightly/deployWorkspaceProject/scenarios/polyrepo/basicJSScenario.ts @@ -5,15 +5,15 @@ import { randomUtils } from "@microsoft/vscode-azext-utils"; import * as path from "path"; -import { type DeploymentConfigurationSettings } from "../../../../extension.bundle"; -import { type StringOrRegExpProps } from "../../../typeUtils"; -import { dwpTestUtils } from "../dwpTestUtils"; -import { type DeployWorkspaceProjectTestCase } from "./DeployWorkspaceProjectTestCase"; +import { type DeploymentConfigurationSettings } from "../../../../../extension.bundle"; +import { type StringOrRegExpProps } from "../../../../typeUtils"; +import { dwpTestUtils } from "../../dwpTestUtils"; +import { type DeployWorkspaceProjectTestCase } from "../DeployWorkspaceProjectTestScenario"; -export function generateAlbumApiJavaScriptTestCases(): DeployWorkspaceProjectTestCase[] { - const folderName: string = 'albumapi-js'; - const appResourceName: string = 'album-api'; - const sharedResourceName: string = 'album-js' + randomUtils.getRandomHexString(4); +export function generateBasicJSTests(): DeployWorkspaceProjectTestCase[] { + const folderName: string = 'basic-js'; + const appResourceName: string = 'b-js-app'; + const sharedResourceName: string = 'b-js-env' + randomUtils.getRandomHexString(4); const acrResourceName: string = sharedResourceName.replace(/[^a-zA-Z0-9]+/g, ''); return [ @@ -21,16 +21,15 @@ export function generateAlbumApiJavaScriptTestCases(): DeployWorkspaceProjectTes label: 'Should fail to deploy app (bad Dockerfile)', inputs: [ new RegExp(folderName, 'i'), - path.join('src', 'test_fail.Dockerfile'), - new RegExp('Create new container apps environment', 'i'), - new RegExp('Create new container registry', 'i'), - 'Continue', - sharedResourceName.slice(0, -1), // Isolate by using a different resource group name since we expect this case to fail + 'Basic', + sharedResourceName, appResourceName, - `.${path.sep}src`, + 'East US', 'Docker Login Credentials', 'Enable', - 'East US', + path.join('src', 'test_fail.Dockerfile'), + `.${path.sep}src`, + 'Continue', 'Save' ], expectedResults: undefined, @@ -42,22 +41,21 @@ export function generateAlbumApiJavaScriptTestCases(): DeployWorkspaceProjectTes label: 'Deploy App', inputs: [ new RegExp(folderName, 'i'), - path.join('src', 'Dockerfile'), - new RegExp('Create new container apps environment', 'i'), - new RegExp('Create new container registry', 'i'), - 'Continue', + 'Basic', sharedResourceName, appResourceName, - `.${path.sep}src`, + 'East US', 'Docker Login Credentials', 'Enable', - 'East US', + path.join('src', 'Dockerfile'), + `.${path.sep}src`, + 'Continue', 'Save' ], expectedResults: dwpTestUtils.generateExpectedResultsWithCredentials(sharedResourceName, acrResourceName, appResourceName), expectedVSCodeSettings: { deploymentConfigurations: [ - generateExpectedDeploymentConfiguration(sharedResourceName, acrResourceName, appResourceName) + generateExpectedJSDeploymentConfiguration(sharedResourceName, acrResourceName, appResourceName) ] }, postTestAssertion: dwpTestUtils.generatePostTestAssertion({ targetPort: 8080, env: undefined }), @@ -73,7 +71,7 @@ export function generateAlbumApiJavaScriptTestCases(): DeployWorkspaceProjectTes expectedResults: dwpTestUtils.generateExpectedResultsWithCredentials(sharedResourceName, acrResourceName, appResourceName), expectedVSCodeSettings: { deploymentConfigurations: [ - generateExpectedDeploymentConfiguration(sharedResourceName, acrResourceName, appResourceName) + generateExpectedJSDeploymentConfiguration(sharedResourceName, acrResourceName, appResourceName) ] }, postTestAssertion: dwpTestUtils.generatePostTestAssertion({ targetPort: 8080, env: undefined }) @@ -81,7 +79,7 @@ export function generateAlbumApiJavaScriptTestCases(): DeployWorkspaceProjectTes ]; } -function generateExpectedDeploymentConfiguration(sharedResourceName: string, acrResourceName: string, appResourceName: string): StringOrRegExpProps { +export function generateExpectedJSDeploymentConfiguration(sharedResourceName: string, acrResourceName: string, appResourceName: string): StringOrRegExpProps { return { label: appResourceName, type: 'AcrDockerBuildRequest', @@ -90,6 +88,6 @@ function generateExpectedDeploymentConfiguration(sharedResourceName: string, acr envPath: '', resourceGroup: sharedResourceName, containerApp: appResourceName, - containerRegistry: new RegExp(`${acrResourceName}.{6}`, 'i'), + containerRegistry: new RegExp(acrResourceName, 'i'), }; } diff --git a/test/nightly/deployWorkspaceProject/scenarios/testScenarios.ts b/test/nightly/deployWorkspaceProject/scenarios/testScenarios.ts new file mode 100644 index 000000000..bebf3f5e5 --- /dev/null +++ b/test/nightly/deployWorkspaceProject/scenarios/testScenarios.ts @@ -0,0 +1,45 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See LICENSE.md in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { longRunningLocalTestsEnabled } from "../../../global.test"; +import { type DeployWorkspaceProjectTestScenario } from "./DeployWorkspaceProjectTestScenario"; +import { generateMonorepoAdminCredentialsTests } from "./monorepo/adminCredentialsScenario"; +import { generateMonorepoIdentityTests } from "./monorepo/identityScenario"; +import { generateAdvancedJSTests } from "./polyrepo/advancedJSScenario"; +import { generateBasicJSTests } from "./polyrepo/basicJSScenario"; + +export function generateTestScenarios(): DeployWorkspaceProjectTestScenario[] { + const testScenarios: DeployWorkspaceProjectTestScenario[] = [ + { + label: 'basic-js', + folderName: 'basic-js', + testCases: generateBasicJSTests(), + }, + { + label: 'advanced-js', + folderName: 'advanced-js', + testCases: generateAdvancedJSTests(), + }, + { + label: 'monorepo-admincreds', + folderName: 'monorepo-admincreds', + testCases: generateMonorepoAdminCredentialsTests(), + }, + ]; + + if (longRunningLocalTestsEnabled) { + // Insufficient auth privilege to test managed identity / role assignment in our manual testing subscription. + // Therefore, limit these tests to only run locally in personal subscriptions where user has full permission to assign roles. + // Todo: Investigate if it makes sense to elevate remote privileges such that these tests can also be automated to run remotely. + testScenarios.push({ + label: 'monorepo-identity', + folderName: 'monorepo-identity', + testCases: generateMonorepoIdentityTests(), + }); + } + + return testScenarios; +} + diff --git a/test/nightly/deployWorkspaceProject/testCases/monoRepoTestCases/generateExpectedDeploymentConfiguration.ts b/test/nightly/deployWorkspaceProject/testCases/monoRepoTestCases/generateExpectedDeploymentConfiguration.ts deleted file mode 100644 index d9b226da3..000000000 --- a/test/nightly/deployWorkspaceProject/testCases/monoRepoTestCases/generateExpectedDeploymentConfiguration.ts +++ /dev/null @@ -1,16 +0,0 @@ -import * as path from "path"; -import { type DeploymentConfigurationSettings } from "../../../../../extension.bundle"; -import { type StringOrRegExpProps } from "../../../../typeUtils"; - -export function generateExpectedDeploymentConfiguration(sharedResourceName: string, acrResourceName: string, appResourceName: string): StringOrRegExpProps { - return { - label: appResourceName, - type: 'AcrDockerBuildRequest', - dockerfilePath: path.join(appResourceName, 'Dockerfile'), - srcPath: appResourceName, - envPath: path.join(appResourceName, '.env.example'), - resourceGroup: sharedResourceName, - containerApp: appResourceName, - containerRegistry: new RegExp(`${acrResourceName}.{6}`, 'i'), - }; -} diff --git a/test/testProjects/advanced-js/src/test_fail.Dockerfile b/test/testProjects/advanced-js/src/test_fail.Dockerfile new file mode 100644 index 000000000..a427f0699 --- /dev/null +++ b/test/testProjects/advanced-js/src/test_fail.Dockerfile @@ -0,0 +1,10 @@ +FROM node:lts-alpine +ENV NODE_ENV=production +WORKDIR /usr/src/app +# COPY ["package.json", "package-lock.json*", "npm-shrinkwrap.json*", "./"] +RUN npm install --production --silent && mv node_modules ../ +COPY . . +EXPOSE 8080 +RUN chown -R node /usr/src/app +USER node +CMD ["npm", "start"]