|
| 1 | +/*! |
| 2 | + * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. |
| 3 | + * SPDX-License-Identifier: Apache-2.0 |
| 4 | + */ |
| 5 | + |
| 6 | +import * as assert from 'assert' |
| 7 | +import { |
| 8 | + CodeCatalystClient, |
| 9 | + CodeCatalystOrg, |
| 10 | + CodeCatalystProject, |
| 11 | + createClient as createCodeCatalystClient, |
| 12 | + DevEnvironment, |
| 13 | +} from '../../shared/clients/codecatalystClient' |
| 14 | +import { DeleteDevEnvironmentResponse } from 'aws-sdk/clients/codecatalyst' |
| 15 | +import { prepareDevEnvConnection } from '../../codecatalyst/model' |
| 16 | +import { Auth, SsoConnection } from '../../credentials/auth' |
| 17 | +import { CodeCatalystAuthenticationProvider, isValidCodeCatalystConnection } from '../../codecatalyst/auth' |
| 18 | +import { CodeCatalystCommands, DevEnvironmentSettings } from '../../codecatalyst/commands' |
| 19 | +import globals from '../../shared/extensionGlobals' |
| 20 | + |
| 21 | +let spaceName: CodeCatalystOrg['name'] |
| 22 | +let projectName: CodeCatalystProject['name'] |
| 23 | +let auth: Auth |
| 24 | + |
| 25 | +/** |
| 26 | + * Key Information: |
| 27 | + * |
| 28 | + * This test can be run locally under the following conditions: |
| 29 | + * |
| 30 | + * - You have previously configured BuilderId using the vscode extension. |
| 31 | + * This is because this test uses BuilderId/SSO information |
| 32 | + * that the extension would have naturally created in that process. |
| 33 | + * |
| 34 | + * - A Space already exists in your Code Catalyst account. |
| 35 | + * We cannot currently automate this due to space creation |
| 36 | + * not being available in the Code Catalyst API. |
| 37 | + * |
| 38 | + * TODO: Create a new space instead of using an existing one, |
| 39 | + * if the api eventually allows us to do so. |
| 40 | + * |
| 41 | + * - You do not have a Code Catalyst project with the same name |
| 42 | + * as {@link projectName}. This project may be modified when |
| 43 | + * this test is run. |
| 44 | + * |
| 45 | + * The test project cannot be torn down. |
| 46 | + * |
| 47 | + * - The code catalyst api does not provide a way to delete |
| 48 | + * a Project. So the project will exist in the Space unless |
| 49 | + * deleted manually. |
| 50 | + * |
| 51 | + * TODO: Delete the project when the test is done, |
| 52 | + * if the api eventually allows us to do so. |
| 53 | + * |
| 54 | + * This provides a best effort to test the api functionality. |
| 55 | + * |
| 56 | + * - There are more complexities involved in testing api functionality |
| 57 | + * as if we were using the vscode UI, so this tests the underlying CC |
| 58 | + * api functionliaty. |
| 59 | + * |
| 60 | + * TODO: We can borrow some components from `src/test/globalSetup.test.ts` |
| 61 | + * to be able to leverage `getTestWindow()` and then use that |
| 62 | + * for UI actions testing. |
| 63 | + * |
| 64 | + * TODO: Create separate tests that spin up a vscode extensionHost |
| 65 | + * integ tests, but using the ssh hostname that we get from |
| 66 | + * {@link prepareDevEnvConnection}. |
| 67 | + */ |
| 68 | +describe('Test how this codebase uses the Code Catalyst API', function () { |
| 69 | + let client: CodeCatalystClient |
| 70 | + let commands: CodeCatalystCommands |
| 71 | + |
| 72 | + before(async function () { |
| 73 | + if (process.env['AWS_TOOLKIT_AUTOMATION'] !== 'local') { |
| 74 | + this.skip() |
| 75 | + } |
| 76 | + |
| 77 | + commands = await createTestCodeCatalystCommands() |
| 78 | + |
| 79 | + auth = Auth.instance |
| 80 | + client = await createTestCodeCatalystClient() |
| 81 | + spaceName = (await getCurrentUsersSpace()).name |
| 82 | + projectName = (await tryCreateTestProject(spaceName)).name |
| 83 | + await deleteExistingDevEnvs(projectName) |
| 84 | + }) |
| 85 | + |
| 86 | + /** |
| 87 | + * Returns the existing Sso connection that has been |
| 88 | + * verified to work with Code Catalyst. |
| 89 | + * |
| 90 | + * This relies on SSO information already being configured. |
| 91 | + */ |
| 92 | + async function getCodeCatalystSsoConnection(): Promise<SsoConnection> { |
| 93 | + const builderIdSsoConnection = (await auth.listConnections()).find(isValidCodeCatalystConnection) |
| 94 | + assert.ok(builderIdSsoConnection, 'To fix, setup Builder Id as if you were a user of the extension.') |
| 95 | + return builderIdSsoConnection |
| 96 | + } |
| 97 | + |
| 98 | + /** |
| 99 | + * Creates a code catalyst commands instance. |
| 100 | + * |
| 101 | + * This holds the underlying functions that are triggered |
| 102 | + * when the user interacts with the vscode UI model. |
| 103 | + * |
| 104 | + * The goal is to test using this object as much as we can, |
| 105 | + * so we can test as close as we can to the user experience. |
| 106 | + * This can have interactions with vscode UI model. |
| 107 | + */ |
| 108 | + async function createTestCodeCatalystCommands(): Promise<CodeCatalystCommands> { |
| 109 | + const ccAuthProvider = CodeCatalystAuthenticationProvider.fromContext(globals.context) |
| 110 | + return new CodeCatalystCommands(ccAuthProvider) |
| 111 | + } |
| 112 | + |
| 113 | + /** |
| 114 | + * Creates a code catalyst api client. |
| 115 | + */ |
| 116 | + async function createTestCodeCatalystClient(): Promise<CodeCatalystClient> { |
| 117 | + const conn = await getCodeCatalystSsoConnection() |
| 118 | + return await createCodeCatalystClient(conn) |
| 119 | + } |
| 120 | + |
| 121 | + /** |
| 122 | + * Creates a specific CC Project if it does not already exist in the space. |
| 123 | + */ |
| 124 | + async function tryCreateTestProject(spaceName: CodeCatalystOrg['name']): Promise<CodeCatalystProject> { |
| 125 | + // IMPORTANT: Be careful changing this if you plan to run locally. |
| 126 | + const projectName = 'aws-vscode-toolkit-integ-test-project' |
| 127 | + |
| 128 | + return client.createProject({ |
| 129 | + spaceName, |
| 130 | + displayName: projectName, |
| 131 | + description: 'This project is autogenerated by the AWS Toolkit VSCode Integ Test.', |
| 132 | + }) |
| 133 | + } |
| 134 | + |
| 135 | + /** |
| 136 | + * Gets the first code catalyst space it finds. |
| 137 | + * |
| 138 | + * The intention for this is to require no setup of a Space by the |
| 139 | + * user if they want to run this test locally. |
| 140 | + */ |
| 141 | + async function getCurrentUsersSpace(): Promise<CodeCatalystOrg> { |
| 142 | + const firstPageOfSpaces = (await client.listSpaces().iterator().next()).value |
| 143 | + |
| 144 | + if (firstPageOfSpaces === undefined || firstPageOfSpaces.length === 0) { |
| 145 | + // Space must already exist due to CC not providing an api to create a space. |
| 146 | + throw new Error( |
| 147 | + 'No spaces found in account. A Code Catalyst Space must be created manually before running this test.' |
| 148 | + ) |
| 149 | + } |
| 150 | + |
| 151 | + const firstSpaceFound = firstPageOfSpaces[0] |
| 152 | + return firstSpaceFound |
| 153 | + } |
| 154 | + |
| 155 | + /** |
| 156 | + * Deletes any existing dev envs from the given project. |
| 157 | + */ |
| 158 | + async function deleteExistingDevEnvs(projectName: CodeCatalystProject['name']): Promise<void> { |
| 159 | + const currentDevEnvs = await client |
| 160 | + .listDevEnvironments({ name: projectName, org: { name: spaceName }, type: 'project' }) |
| 161 | + .flatten() |
| 162 | + .promise() |
| 163 | + await Promise.all(currentDevEnvs.map(async devEnv => deleteDevEnv(devEnv.id))) |
| 164 | + } |
| 165 | + |
| 166 | + /** |
| 167 | + * Deletes a specific dev env |
| 168 | + */ |
| 169 | + function deleteDevEnv(id: DevEnvironment['id']): Promise<DeleteDevEnvironmentResponse> { |
| 170 | + return client.deleteDevEnvironment({ |
| 171 | + spaceName, |
| 172 | + projectName, |
| 173 | + id: id, |
| 174 | + }) |
| 175 | + } |
| 176 | + |
| 177 | + describe('Dev Environment apis', function () { |
| 178 | + let devEnv: DevEnvironment |
| 179 | + let devEnvSettings: DevEnvironmentSettings |
| 180 | + |
| 181 | + function createDevEnv(): Promise<DevEnvironment> { |
| 182 | + devEnvSettings = { |
| 183 | + instanceType: 'dev.standard1.small', |
| 184 | + persistentStorage: { sizeInGiB: 16 }, |
| 185 | + alias: `test-alias-${Date.now()}`, |
| 186 | + } |
| 187 | + return client.createDevEnvironment({ |
| 188 | + spaceName, |
| 189 | + projectName, |
| 190 | + ides: [{ name: 'VSCode' }], |
| 191 | + ...devEnvSettings, |
| 192 | + }) |
| 193 | + } |
| 194 | + |
| 195 | + before(async function () { |
| 196 | + devEnv = await createDevEnv() |
| 197 | + }) |
| 198 | + |
| 199 | + after(async function () { |
| 200 | + await deleteDevEnv(devEnv.id) |
| 201 | + }) |
| 202 | + |
| 203 | + it('can succesfully create a Dev Environment', async function () { |
| 204 | + assert.strictEqual(devEnv.project.name, projectName) |
| 205 | + assert.strictEqual(devEnv.org.name, spaceName) |
| 206 | + }) |
| 207 | + |
| 208 | + it('can ssh in to a Dev Environment', async function () { |
| 209 | + // Get necessary objects to run the ssh command. |
| 210 | + const { SessionProcess, hostname, sshPath } = await prepareDevEnvConnection(client, { ...devEnv }) |
| 211 | + |
| 212 | + // Through ssh, run 'ls' command in the dev env. |
| 213 | + const lsOutput = (await new SessionProcess(sshPath, [hostname, 'ls', '/projects']).run()).stdout |
| 214 | + |
| 215 | + // Assert that a certain file exists in the dev env. |
| 216 | + const expectedFile = 'devfile.yaml' |
| 217 | + assert(lsOutput.split('\n').includes(expectedFile)) // File automatically created by CC |
| 218 | + }) |
| 219 | + |
| 220 | + it('can update an existing dev environment', async function () { |
| 221 | + // Ensure current alias name is expected |
| 222 | + assert.strictEqual(devEnv.alias, devEnvSettings.alias) |
| 223 | + assert.strictEqual(devEnv.instanceType, devEnvSettings.instanceType) |
| 224 | + |
| 225 | + // Update dev env |
| 226 | + const devEnvSettingsToUpdate = { alias: `test-alias-${Date.now()}`, instanceType: 'dev.standard1.medium' } |
| 227 | + const updatedDevEnv = await commands.updateDevEnv(devEnv, devEnvSettingsToUpdate) |
| 228 | + |
| 229 | + // Ensure our update succeeded by checking for new properties |
| 230 | + assert.strictEqual(updatedDevEnv?.alias, devEnvSettingsToUpdate.alias) |
| 231 | + assert.strictEqual(updatedDevEnv?.instanceType, devEnvSettingsToUpdate.instanceType) |
| 232 | + }) |
| 233 | + }) |
| 234 | +}) |
0 commit comments