Skip to content

Commit

Permalink
test(integration): classify types of tests, run integ in test:all (#4647
Browse files Browse the repository at this point in the history
)

* test(integration): ec2 integ tests, classify integ tests, run integ in test:all

* test(client-ec2): refactor ec2 e2e tests

* test(middleware-sdk-rds): add RDS integration tests
  • Loading branch information
kuhe committed Apr 22, 2023
1 parent 1e806ba commit 4490d7a
Show file tree
Hide file tree
Showing 28 changed files with 461 additions and 98 deletions.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion clients/client-transcribe-streaming/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"clean": "rimraf ./dist-* && rimraf *.tsbuildinfo",
"extract:docs": "api-extractor run --local",
"generate:client": "node ../../scripts/generate-clients/single-service --solo transcribe-streaming",
"test:integration": "jest --config jest.integ.config.js"
"test:integration": "jest --config jest.config.integ.js"
},
"main": "./dist-cjs/index.js",
"types": "./dist-types/index.d.ts",
Expand Down
10 changes: 9 additions & 1 deletion features/ec2/ec2.feature
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,18 @@ Feature: Amazon Elastic Compute Cloud
Then the error code should be "MissingParameter"
And the error status code should be 400

Scenario: Encrypted CopySnapshot
Scenario: Encrypted-Unencrypted CopySnapshot
Given I attempt to copy an encrypted snapshot across regions
Then the copy snapshot attempt should be successful

Scenario: Unencrypted-Encrypted CopySnapshot
Given I attempt to copy and encrypt an unencrypted snapshot across regions
Then the copy snapshot attempt should be successful

Scenario: Encrypted-Encrypted CopySnapshot
Given I attempt to copy and encrypt an encrypted snapshot across regions
Then the copy snapshot attempt should be successful

# @pagination
# Scenario: Paginating responses
# Given I paginate the "describeReservedInstancesOfferings" operation with limit 20 and max pages 3
Expand Down
212 changes: 145 additions & 67 deletions features/ec2/step_definitions/ec2.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,8 @@
const { Before, Given, Then } = require("@cucumber/cucumber");

const waitForVolumeAvailableCallback = (ec2, volumeId, callback) => {
const { waitForVolumeAvailable } = require("../../../clients/client-ec2");
waitForVolumeAvailable({ client: ec2 }, { VolumeIds: [volumeId] }).then(
function (data) {
callback();
},
function (err) {
callback(err);
}
);
};
const { EC2, waitUntilSnapshotCompleted, waitUntilVolumeAvailable } = require("../../../clients/client-ec2");
const { KMS } = require("../../../clients/client-kms");

Before({ tags: "@ec2" }, function (scenario, callback) {
const { EC2 } = require("../../../clients/client-ec2");
this.service = new EC2({});
callback();
});
Expand All @@ -34,64 +23,153 @@ Given("I describe the EC2 instance {string}", function (instanceId, callback) {
this.request(null, "describeInstances", { InstanceIds: [instanceId] }, callback, false);
});

Given("I attempt to copy an encrypted snapshot across regions", function (callback) {
const { EC2 } = require("../../../clients/client-ec2");
const self = this;
let volId, srcSnapId, dstSnapId, params;
const sourceRegion = "us-west-2";
const destRegion = "us-east-1";
const srcEc2 = new EC2({ region: sourceRegion });
const dstEc2 = new EC2({ region: destRegion });

function teardown() {
if (volId) srcEc2.deleteVolume({ VolumeId: volId });
if (srcSnapId) srcEc2.deleteSnapshot({ SnapshotId: srcSnapId });
if (dstSnapId) dstEc2.deleteSnapshot({ SnapshotId: dstSnapId });
}

params = {
AvailabilityZone: sourceRegion + "a",
Size: 10,
Encrypted: true,
};
srcEc2.createVolume(params, function (err, data) {
if (err) {
teardown();
return callback(err);
}
volId = data.VolumeId;
Given(
"I attempt to copy an encrypted snapshot across regions",
copySnapshotIntegration({ encryptSource: true, encryptDestination: false })
);

waitForVolumeAvailableCallback(srcEc2, volId, function (err) {
if (err) {
teardown();
return callback(err);
}
Given(
"I attempt to copy and encrypt an unencrypted snapshot across regions",
copySnapshotIntegration({ encryptSource: false, encryptDestination: true })
);

srcEc2.createSnapshot({ VolumeId: volId }, function (err, data) {
if (err) {
teardown();
return callback(err);
}
srcSnapId = data.SnapshotId;

setTimeout(function () {
params = {
SourceRegion: sourceRegion,
SourceSnapshotId: srcSnapId,
};
dstEc2.copySnapshot(params, function (err, data) {
if (data) dstSnapId = data.SnapshotId;
self.success = true;
callback();
teardown();
});
}, 5000);
});
});
});
});
Given(
"I attempt to copy and encrypt an encrypted snapshot across regions",
copySnapshotIntegration({ encryptSource: true, encryptDestination: true })
);

Then("the copy snapshot attempt should be successful", function (callback) {
this.assert.equal(this.success, true);
callback();
});

function copySnapshotIntegration({ encryptSource, encryptDestination } = {}) {
return async function () {
let volId, srcSnapId, dstSnapId, sourceKmsKey, destinationKmsKey;
const sourceRegion = "us-west-2";
const destRegion = "us-east-1";
const srcEc2 = new EC2({ region: sourceRegion });
const dstEc2 = new EC2({ region: destRegion });

const sourceKeyAlias = "js-sdk-e2e-test-key-1-source";
const destinationKeyAlias = "js-sdk-e2e-test-key-2-destination";

const sourceKms = new KMS({ region: sourceRegion });
const destinationKms = new KMS({ region: destRegion });

function teardown() {
if (volId) srcEc2.deleteVolume({ VolumeId: volId });
if (srcSnapId) srcEc2.deleteSnapshot({ SnapshotId: srcSnapId });
if (dstSnapId) dstEc2.deleteSnapshot({ SnapshotId: dstSnapId });
}

try {
if (encryptSource) {
sourceKmsKey = await getKey(sourceKms, sourceKeyAlias);
}
if (encryptDestination) {
destinationKmsKey = await getKey(destinationKms, destinationKeyAlias);
}

const createVolume = await srcEc2.createVolume({
AvailabilityZone: sourceRegion + "a",
TagSpecifications: [
{
Tags: [{ Key: "aws-sdk-js-integration", Value: "SAFE_TO_DELETE" }],
ResourceType: "volume",
},
],
Size: 1,
Encrypted: encryptSource ? true : undefined,
KmsKeyId: encryptSource ? sourceKmsKey : undefined,
});

volId = createVolume.VolumeId;

await waitUntilVolumeAvailable({ client: srcEc2, maxWaitTime: 60 }, { VolumeIds: [volId] });
const createSnapshot = await srcEc2.createSnapshot({
VolumeId: volId,
TagSpecifications: [
{
Tags: [{ Key: "aws-sdk-js-integration", Value: "SAFE_TO_DELETE" }],
ResourceType: "snapshot",
},
],
});

srcSnapId = createSnapshot.SnapshotId;

await waitUntilSnapshotCompleted(
{ client: srcEc2, maxWaitTime: 20 },
{
SnapshotId: createSnapshot.SnapshotId,
}
);

const copySnapshot = await dstEc2.copySnapshot({
Description: "aws-sdk-js-integration safe-to-delete",
SourceRegion: sourceRegion,
SourceSnapshotId: srcSnapId,
KmsKeyId: encryptDestination ? destinationKmsKey : undefined,
Encrypted: encryptDestination ? true : undefined,
TagSpecifications: [
{
Tags: [{ Key: "aws-sdk-js-integration", Value: "SAFE_TO_DELETE" }],
ResourceType: "snapshot",
},
],
});
dstSnapId = copySnapshot.SnapshotId;

// this waiter is not doing what I think it does for copied snapshot in destination region.
// await waitUntilSnapshotCompleted(
// { client: dstEc2, maxWaitTime: 20 },
// {
// SnapshotId: copySnapshot.SnapshotId,
// }
// );
// until corrected, use timeout waiter.
await new Promise((r) => setTimeout(r, 5000));

const describeSnapshots = await dstEc2.describeSnapshots({
SnapshotIds: [copySnapshot.SnapshotId],
});

if (encryptDestination) {
if (String(describeSnapshots.Snapshots[0].KmsKeyId).includes(destinationKmsKey)) {
this.success = true;
}
} else {
this.success = true;
}

teardown();
} catch (err) {
teardown();
throw err;
}
};
}

/**
* @param {KMS} kmsClient
* @param {string} alias
* @returns
*/
async function getKey(kmsClient, alias) {
return kmsClient
.describeKey({
KeyId: "alias/" + alias,
})
.then((describeKey) => describeKey.KeyMetadata.KeyId)
.catch(async () => {
const createKey = await kmsClient.createKey({
MultiRegion: true,
});
await kmsClient.createAlias({
AliasName: "alias/" + alias,
TargetKeyId: createKey.KeyMetadata.KeyId,
});
return createKey.KeyMetadata.KeyId;
});
}
2 changes: 1 addition & 1 deletion jest.config.base.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const { compilerOptions } = require("@tsconfig/recommended/tsconfig.json");

module.exports = {
preset: "ts-jest",
testMatch: ["**/*.spec.ts", "!**/*.browser.spec.ts", "!**/*.integ.spec.ts"],
testMatch: ["**/*.spec.ts", "!**/*.browser.spec.ts", "!**/*.integ.spec.ts", "!**/*.e2e.spec.ts"],
globals: {
"ts-jest": {
tsconfig: {
Expand Down
14 changes: 14 additions & 0 deletions jest.config.e2e.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* E2E tests are integration tests that connect to live services
* and should be run with the yarn test:e2e script in each package.
*
* There is also a large group of E2E tests in the features folder, using cucumber-js.
*/
module.exports = {
projects: [
// "<rootDir>/clients/*/jest.config.e2e.js",
// "<rootDir>/lib/*/jest.config.integ.js",
"<rootDir>/packages/*/jest.config.e2e.js",
// "<rootDir>/private/*/jest.config.e2e.js",
],
};
16 changes: 15 additions & 1 deletion jest.config.integ.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
/**
* Integration tests are tests that require multiple units or packages,
* and do not connect to live services.
* These should be run with the yarn test:integration script in each package.
* For tests that involve network requests to live services, see jest.config.e2e.js.
*
* The "test:integration:legacy" tests run with cucumber-js should be considered
* E2E tests in this classification system.
*/
module.exports = {
projects: ["<rootDir>/clients/*/jest.integ.config.js", "<rootDir>/packages/*/jest.config.integ.js"],
projects: [
"<rootDir>/clients/*/jest.config.integ.js",
// "<rootDir>/lib/*/jest.config.integ.js",
"<rootDir>/packages/*/jest.config.integ.js",
"<rootDir>/private/*/jest.config.integ.js",
],
};
5 changes: 5 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
/**
* Unit tests are test scenarios that exercise a single functionality.
* For multiple module integration, see jest.config.integ.js.
* For tests that involve network requests to live services, see jest.config.e2e.js.
*/
module.exports = {
projects: [
"<rootDir>/lib/*/jest.config.js",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"lint:ci": "lerna exec --since origin/main --exclude-dependents --ignore '@aws-sdk/client-*' --ignore '@aws-sdk/aws-*' 'eslint --quiet src/**/*.ts'",
"lint:release": "lerna exec --ignore '@aws-sdk/client-*' --ignore '@aws-sdk/aws-*' 'eslint --quiet src/**/*.ts'",
"local-publish": "node ./scripts/verdaccio-publish/index.js",
"test:all": "yarn build:all && jest --coverage --passWithNoTests && lerna run test --scope '@aws-sdk/{fetch-http-handler,hash-blob-browser}' && yarn test:versions",
"test:all": "yarn build:all && jest --coverage --passWithNoTests && lerna run test --scope '@aws-sdk/{fetch-http-handler,hash-blob-browser}' && yarn test:versions && yarn test:integration",
"test:ci": "lerna run test --since origin/main",
"test:e2e": "yarn build:e2e && node ./tests/e2e/index.js",
"test:functional": "jest --passWithNoTests --config tests/functional/jest.config.js && lerna run test:unit --scope \"@aws-sdk/client-*\"",
Expand Down
3 changes: 2 additions & 1 deletion packages/middleware-sdk-ec2/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"build:types": "tsc -p tsconfig.types.json",
"build:types:downlevel": "downlevel-dts dist-types dist-types/ts3.4",
"clean": "rimraf ./dist-* && rimraf *.tsbuildinfo",
"test": "jest"
"test": "jest",
"test:integration": "jest --config jest.config.integ.js"
},
"main": "./dist-cjs/index.js",
"module": "./dist-es/index.js",
Expand Down
Loading

0 comments on commit 4490d7a

Please sign in to comment.