Skip to content

Commit 01ff860

Browse files
authored
test: deploy integration test stack automatically and new test scripts (#2944)
* chore: add ci script for building changed packages * chore: reorder ci scripts * chore: skip integ test if no client changes * test(e2e): deploy integration test stack automatically * test(e2e): use node10 syntax * test(e2e): exit script with 0 code after complete This is to fix the segment fault issue in the Node.js 12 e2e test in CodeBuild runtime after returning. This issue cannot be reproduced in other Node.js version or locally.
1 parent a9ad7f6 commit 01ff860

File tree

6 files changed

+106
-10
lines changed

6 files changed

+106
-10
lines changed

package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,21 @@
1010
"generate-clients": "node ./scripts/generate-clients",
1111
"generate:clients:generic": "node ./scripts/generate-clients/generic",
1212
"bootstrap": "yarn",
13+
"bootstrap:ci": "yarn install --frozen-lockfile",
1314
"clean": "yarn clear-build-cache && yarn clear-build-info && lerna clean",
1415
"clear-build-cache": "rimraf ./packages/*/dist ./clients/*/dist ./lib/*/dist ./private/*/dist",
1516
"clear-build-info": "rimraf ./packages/**/*.tsbuildinfo ./clients/**/*.tsbuildinfo ./lib/**/*.tsbuildinfo ./private/**/*.tsbuildinfo",
1617
"remove-documentation": "rimraf ./docs",
1718
"build:clients:generic": "lerna run --scope '@aws-sdk/aws-echo-service' --include-dependencies build",
19+
"build:packages": "lerna run build --ignore '@aws-sdk/client-*' --ignore '@aws-sdk/aws-*' --ignore '@aws-sdk/lib-*' --include-dependencies",
1820
"build:crypto-dependencies": "lerna run --scope '@aws-sdk/{types,util-utf8-browser,util-locate-window,hash-node}' --include-dependencies build",
1921
"build:protocols": "yarn build:crypto-dependencies && lerna run --scope '@aws-sdk/aws-protocoltests-*' --include-dependencies build",
2022
"build:server-protocols": "yarn build:crypto-dependencies && lerna run --scope '@aws-sdk/*-server' --include-dependencies build",
23+
"build:clients:since:release": "yarn ci:build:packages && lerna run build --since --scope '@aws-sdk/client-*' --scope '@aws-sdk/lib-*'",
2124
"build:all": "yarn build:crypto-dependencies && lerna run build",
2225
"build-documentation": "yarn remove-documentation && typedoc",
2326
"pretest:all": "yarn build:all",
24-
"test:all": "jest --coverage --passWithNoTests && lerna run test --scope '@aws-sdk/{fetch-http-handler,hash-blob-browser}'",
27+
"test:all": "jest --coverage --passWithNoTests && lerna run test --scope '@aws-sdk/{fetch-http-handler,hash-blob-browser}' && yarn test:versions",
2528
"test:functional": "jest --config tests/functional/jest.config.js",
2629
"test:integration:legacy": "cucumber-js --fail-fast",
2730
"test:integration:legacy:since:release": "./tests/integ-legacy/index.js",
@@ -32,7 +35,8 @@
3235
"test:e2e": "node ./tests/e2e/index.js",
3336
"test:versions": "jest --config tests/versions/jest.config.js tests/versions/index.spec.ts",
3437
"local-publish": "node ./scripts/verdaccio-publish/index.js",
35-
"lerna:version": "lerna version --exact --conventional-commits --no-push --no-git-tag-version --no-commit-hooks --loglevel silent --no-private --yes"
38+
"lerna:version": "lerna version --exact --conventional-commits --no-push --no-git-tag-version --no-commit-hooks --loglevel silent --no-private --yes",
39+
"test:unit": "jest --config jest.config.js"
3640
},
3741
"repository": {
3842
"type": "git",

tests/e2e/ensure-test-stack.js

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
const {
2+
CreateChangeSetCommand,
3+
DescribeStacksCommand,
4+
waitUntilChangeSetCreateComplete,
5+
Capability,
6+
ExecuteChangeSetCommand,
7+
waitUntilStackUpdateComplete,
8+
waitUntilStackCreateComplete,
9+
DescribeChangeSetCommand,
10+
} = require("../../clients/client-cloudformation");
11+
12+
/**
13+
* Deploy the integration test stack if it does not exist. Update the
14+
* stack if there's a changeset. There should be a high level library for it
15+
* really.
16+
*/
17+
exports.ensureTestStack = async (client, stackName, templateBody) => {
18+
let hasExistingStack = false;
19+
try {
20+
await client.send(new DescribeStacksCommand({ StackName: stackName }));
21+
hasExistingStack = true;
22+
} catch (e) {
23+
if ((e.message || "").endsWith("does not exist")) {
24+
hasExistingStack = false;
25+
} else {
26+
throw e;
27+
}
28+
}
29+
30+
const { Id } = await client.send(
31+
new CreateChangeSetCommand({
32+
StackName: stackName,
33+
ChangeSetType: hasExistingStack ? "UPDATE" : "CREATE",
34+
ChangeSetName: `${stackName}ChangeSet`,
35+
TemplateBody: templateBody,
36+
Capabilities: [Capability.CAPABILITY_IAM],
37+
})
38+
);
39+
40+
try {
41+
// TODO: Feature Request: return the last describe response from the waiter result, so we can inspect failure reasons.
42+
await waitUntilChangeSetCreateComplete({ client }, { ChangeSetName: Id });
43+
} catch (e) {
44+
const { Status, StatusReason } = await client.send(
45+
new DescribeChangeSetCommand({
46+
StackName: stackName,
47+
ChangeSetName: Id,
48+
})
49+
);
50+
if (Status === "FAILED" && StatusReason.includes("The submitted information didn't contain changes")) {
51+
return;
52+
}
53+
throw e;
54+
}
55+
56+
await client.send(
57+
new ExecuteChangeSetCommand({
58+
ChangeSetName: Id,
59+
StackName: stackName,
60+
})
61+
);
62+
if (hasExistingStack) {
63+
await waitUntilStackUpdateComplete({ client }, { StackName: stackName });
64+
} else {
65+
await waitUntilStackCreateComplete({ client }, { StackName: stackName });
66+
}
67+
};

tests/e2e/get-integ-test-resources.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,24 @@
1+
const { readFileSync } = require("fs");
2+
const { join } = require("path");
13
const { CloudFormationClient, DescribeStackResourcesCommand } = require("../../clients/client-cloudformation");
4+
const { ensureTestStack } = require("./ensure-test-stack");
25

36
exports.getIntegTestResources = async () => {
4-
const client = new CloudFormationClient({});
7+
const client = new CloudFormationClient({ logger: console });
58
const region = await client.config.region();
9+
const stackName = "SdkReleaseV3IntegTestResourcesStack";
610

11+
await ensureTestStack(
12+
client,
13+
stackName,
14+
readFileSync(join(__dirname, "IntegTestResourcesStack.template.json"), { encoding: "utf-8" })
15+
);
716
const { StackResources: stackResources } = await client.send(
8-
new DescribeStackResourcesCommand({ StackName: "SdkReleaseV3IntegTestResourcesStack" })
17+
new DescribeStackResourcesCommand({ StackName: stackName })
918
);
1019

20+
console.log(`${stackName} Stack Resources: `, stackResources);
21+
1122
const identityPoolId = stackResources.filter((resource) => resource.ResourceType === "AWS::Cognito::IdentityPool")[0]
1223
.PhysicalResourceId;
1324

tests/e2e/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const run = async () => {
55
try {
66
const integTestResourcesEnv = await getIntegTestResources();
77
await runE2ETests(integTestResourcesEnv);
8+
process.exit(0);
89
} catch (e) {
910
console.error(e);
1011
process.exit(1);

tests/e2e/run-e2e-tests.js

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const { execSync, spawn } = require("child_process");
1+
const { execSync } = require("child_process");
22
const { join } = require("path");
33
const { readFileSync, existsSync } = require("fs");
44
const { spawnPromise } = require("./spawn-promise");
@@ -17,9 +17,12 @@ exports.runE2ETests = async (resourcesEnv) => {
1717
/path/to/package:@aws-sdk/client-acm-pca:1.0.0-gamma.3
1818
/path/to/package:@aws-sdk/client-acm:1.0.0-gamma.3
1919
*/
20-
const changedPackagesRecord = execSync(
21-
"./node_modules/.bin/lerna changed --all --parseable --long --loglevel silent"
22-
);
20+
let changedPackagesRecord = "";
21+
try {
22+
changedPackagesRecord = execSync("./node_modules/.bin/lerna changed --all --parseable --long --loglevel silent");
23+
} catch (e) {
24+
// Swallow error because Lerna throws if no package changes.
25+
}
2326
// Get array for changed package's path
2427
const changedPackages = changedPackagesRecord
2528
.toString()
@@ -28,7 +31,7 @@ exports.runE2ETests = async (resourcesEnv) => {
2831
.map((record) => record.split(":").slice(0, 2));
2932
console.log(changedPackages);
3033
const packagesToTest = changedPackages.filter((changedPackage) => hasE2Etest(changedPackage[0]));
31-
if (packagesToTest?.length === 0) {
34+
if (packagesToTest.length === 0) {
3235
console.log("no changed package contains e2e test.");
3336
return;
3437
}

tests/integ-legacy/index.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,24 @@ const allTags = execSync(`grep -h ^@ ${join(FEATURES_FOLDER, "**", "*.feature")}
1717

1818
console.info(`Looking for changed clients that has the legacy integration test tag: ${allTags}`);
1919

20-
const changedPackages = execSync(`${join(ROOT_BIN, "lerna")} changed`, execOptions).split("\n");
20+
let changedPackages = [];
21+
try {
22+
changedPackages = execSync(`${join(ROOT_BIN, "lerna")} changed`, execOptions).split("\n");
23+
} catch (e) {
24+
// Swallow error because Lerna throws if no package changes.
25+
}
2126
const changedPackageTags = changedPackages
2227
.map((name) => name.replace("@aws-sdk/client-", ""))
2328
.map((name) => name.replace("-"))
2429
.map((name) => `@${name}`);
2530

2631
const tagsToTest = changedPackageTags.filter((tag) => allTags.includes(tag));
2732

33+
if (tagsToTest.length === 0) {
34+
console.info("No clients with integration test cases has changed since last release.");
35+
return;
36+
}
37+
2838
// Cucumber requires cwd to contain the test cases.
2939
const command = `${join("node_modules", ".bin", "cucumber-js")}`;
3040
const args = ["--fail-fast", "-t", `"${tagsToTest.join(" or ")}"`];

0 commit comments

Comments
 (0)