Skip to content

Commit

Permalink
Merge #11292
Browse files Browse the repository at this point in the history
11292: [auto/nodejs] Test remote operations r=justinvp a=justinvp

Also cleans up some error messages to be consistent with the CLI and other languages.

Related:
- #11290
- #11291
- #11293
- #11294

Co-authored-by: Justin Van Patten <jvp@justinvp.com>
  • Loading branch information
bors[bot] and justinvp committed Nov 9, 2022
2 parents c82cb98 + 31bf640 commit f33924e
Show file tree
Hide file tree
Showing 6 changed files with 235 additions and 20 deletions.
2 changes: 1 addition & 1 deletion sdk/nodejs/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ unit_tests:: $(TEST_ALL_DEPS)
yarn run nyc -s mocha 'bin/tests_with_mocks/**/*.spec.js'

test_auto:: $(TEST_ALL_DEPS)
yarn run nyc -s mocha --timeout 120000 'bin/tests/automation/**/*.spec.js'
yarn run nyc -s mocha --timeout 220000 'bin/tests/automation/**/*.spec.js'

TSC_SUPPORTED_VERSIONS = ~3.7.3 ^3 ^4

Expand Down
10 changes: 5 additions & 5 deletions sdk/nodejs/automation/remoteWorkspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,17 +149,17 @@ export interface RemoteWorkspaceOptions {

async function createLocalWorkspace(args: RemoteGitProgramArgs, opts?: RemoteWorkspaceOptions): Promise<LocalWorkspace> {
if (!isFullyQualifiedStackName(args.stackName)) {
throw new Error(`"${args.stackName}" stack name must be fully qualified`);
throw new Error(`stack name "${args.stackName}" must be fully qualified.`);
}

if (!args.url) {
throw new Error("url is required.");
}
if (args.commitHash && args.branch) {
throw new Error("commitHash and branch cannot both be specified.");
if (args.branch && args.commitHash) {
throw new Error("branch and commitHash cannot both be specified.");
}
if (!args.commitHash && !args.branch) {
throw new Error("at least commitHash or branch are required.");
if (!args.branch && !args.commitHash) {
throw new Error("either branch or commitHash is required.");
}
if (args.auth) {
if (args.auth.sshPrivateKey && args.auth.sshPrivateKeyPath) {
Expand Down
14 changes: 1 addition & 13 deletions sdk/nodejs/tests/automation/localWorkspace.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
} from "../../automation";
import { Config, output } from "../../index";
import { asyncTest } from "../util";
import { getTestOrg, getTestSuffix } from "./util";

const versionRegex = /(\d+\.)(\d+\.)(\d+)(-.*)?/;
const userAgent = "pulumi/pulumi/test";
Expand Down Expand Up @@ -887,23 +888,10 @@ describe(`checkVersionIsValid`, () => {
});
});


const getTestSuffix = () => {
return Math.floor(100000 + Math.random() * 900000);
};

const normalizeConfigKey = (key: string, projectName: string) => {
const parts = key.split(":");
if (parts.length < 2) {
return `${projectName}:${key}`;
}
return "";
};

const getTestOrg = () => {
let testOrg = "pulumi-test";
if (process.env.PULUMI_TEST_ORG) {
testOrg = process.env.PULUMI_TEST_ORG;
}
return testOrg;
};
201 changes: 200 additions & 1 deletion sdk/nodejs/tests/automation/remoteWorkspace.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,206 @@

import assert from "assert";

import { isFullyQualifiedStackName } from "../../automation";
import {
fullyQualifiedStackName,
isFullyQualifiedStackName,
LocalWorkspace,
RemoteGitAuthArgs,
RemoteGitProgramArgs,
RemoteStack,
RemoteWorkspace,
RemoteWorkspaceOptions,
} from "../../automation";
import { getTestOrg, getTestSuffix } from "./util";

const testRepo = "https://github.com/pulumi/test-repo.git";

describe("RemoteWorkspace", () => {
describe("selectStack", () => {
describe("throws appropriate errors", () => testErrors(RemoteWorkspace.selectStack));
});

describe("createStack", () => {
describe("throws appropriate errors", () => testErrors(RemoteWorkspace.createStack));

it(`runs through the stack lifecycle`, async function () {
// This test requires the service with access to Pulumi Deployments.
// Set PULUMI_ACCESS_TOKEN to an access token with access to Pulumi Deployments
// and set PULUMI_TEST_DEPLOYMENTS_API to any value to enable the test.
if (!process.env.PULUMI_ACCESS_TOKEN) {
this.skip();
return;
}
if (!process.env.PULUMI_TEST_DEPLOYMENTS_API) {
this.skip();
return;
}

await testLifecycle(RemoteWorkspace.createStack);
});
});

describe("createOrSelectStack", () => {
describe("throws appropriate errors", () => testErrors(RemoteWorkspace.createOrSelectStack));

it(`runs through the stack lifecycle`, async function () {
// This test requires the service with access to Pulumi Deployments.
// Set PULUMI_ACCESS_TOKEN to an access token with access to Pulumi Deployments
// and set PULUMI_TEST_DEPLOYMENTS_API to any value to enable the test.
if (!process.env.PULUMI_ACCESS_TOKEN) {
this.skip();
return;
}
if (!process.env.PULUMI_TEST_DEPLOYMENTS_API) {
this.skip();
return;
}

await testLifecycle(RemoteWorkspace.createOrSelectStack);
});
});
});

function testErrors(fn: (args: RemoteGitProgramArgs, opts?: RemoteWorkspaceOptions) => Promise<RemoteStack>) {
const stack = "owner/project/stack";
const tests: {
name: string;
stackName: string;
url: string;
branch?: string;
commitHash?: string;
auth?: RemoteGitAuthArgs;
error: string;
}[] = [
{
name: "stack empty",
stackName: "",
url: "",
error: `stack name "" must be fully qualified.`,
},
{
name: "stack just name",
stackName: "name",
url: "",
error: `stack name "name" must be fully qualified.`,
},
{
name: "stack just name & owner",
stackName: "owner/name",
url: "",
error: `stack name "owner/name" must be fully qualified.`,
},
{
name: "stack just sep",
stackName: "/",
url: "",
error: `stack name "/" must be fully qualified.`,
},
{
name: "stack just two seps",
stackName: "//",
url: "",
error: `stack name "//" must be fully qualified.`,
},
{
name: "stack just three seps",
stackName: "///",
url: "",
error: `stack name "///" must be fully qualified.`,
},
{
name: "stack invalid",
stackName: "owner/project/stack/wat",
url: "",
error: `stack name "owner/project/stack/wat" must be fully qualified.`,
},
{
name: "no url",
stackName: stack,
url: "",
error: `url is required.`,
},
{
name: "no branch or commit",
stackName: stack,
url: testRepo,
error: `either branch or commitHash is required.`,
},
{
name: "both branch and commit",
stackName: stack,
url: testRepo,
branch: "branch",
commitHash: "commit",
error: `branch and commitHash cannot both be specified.`,
},
{
name: "both ssh private key and path",
stackName: stack,
url: testRepo,
branch: "branch",
auth: {
sshPrivateKey: "key",
sshPrivateKeyPath: "path",
},
error: `sshPrivateKey and sshPrivateKeyPath cannot both be specified.`,
},
];

tests.forEach(test => {
it(`${test.name}`, async () => {
const { stackName, url, branch, commitHash, auth } = test;
await assert.rejects(async () => {
await fn({ stackName, url, branch, commitHash, auth });
}, {
message: test.error,
});
});
});
}

async function testLifecycle(fn: (args: RemoteGitProgramArgs, opts?: RemoteWorkspaceOptions) => Promise<RemoteStack>) {
const stackName = fullyQualifiedStackName(getTestOrg(), "go_remote_proj", `int_test${getTestSuffix()}`);
const stack = await fn({
stackName,
url: testRepo,
branch: "refs/heads/master",
projectPath: "goproj",
},{
preRunCommands: [
`pulumi config set bar abc --stack ${stackName}`,
`pulumi config set --secret buzz secret --stack ${stackName}`,
],
});

// pulumi up
const upRes = await stack.up();
assert.strictEqual(Object.keys(upRes.outputs).length, 3);
assert.strictEqual(upRes.outputs["exp_static"].value, "foo");
assert.strictEqual(upRes.outputs["exp_static"].secret, false);
assert.strictEqual(upRes.outputs["exp_cfg"].value, "abc");
assert.strictEqual(upRes.outputs["exp_cfg"].secret, false);
assert.strictEqual(upRes.outputs["exp_secret"].value, "secret");
assert.strictEqual(upRes.outputs["exp_secret"].secret, true);
assert.strictEqual(upRes.summary.kind, "update");
assert.strictEqual(upRes.summary.result, "succeeded");

// pulumi preview
const preRes = await stack.preview();
assert.strictEqual(preRes.changeSummary.same, 1);

// pulumi refresh
const refRes = await stack.refresh();
assert.strictEqual(refRes.summary.kind, "refresh");
assert.strictEqual(refRes.summary.result, "succeeded");

// pulumi destroy
const destroyRes = await stack.destroy();
assert.strictEqual(destroyRes.summary.kind, "destroy");
assert.strictEqual(destroyRes.summary.result, "succeeded");

await (await LocalWorkspace.create({})).removeStack(stackName);
}

describe("isFullyQualifiedStackName", () => {
const tests = [
Expand Down
27 changes: 27 additions & 0 deletions sdk/nodejs/tests/automation/util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright 2016-2022, Pulumi Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/** @internal */
export function getTestSuffix() {
return Math.floor(100000 + Math.random() * 900000);
}

/** @internal */
export function getTestOrg() {
let testOrg = "pulumi-test";
if (process.env.PULUMI_TEST_ORG) {
testOrg = process.env.PULUMI_TEST_ORG;
}
return testOrg;
}
1 change: 1 addition & 0 deletions sdk/nodejs/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@
"tests/automation/cmd.spec.ts",
"tests/automation/localWorkspace.spec.ts",
"tests/automation/remoteWorkspace.spec.ts",
"tests/automation/util.ts",

"tests_with_mocks/mocks.spec.ts"
]
Expand Down

0 comments on commit f33924e

Please sign in to comment.