Skip to content

Commit

Permalink
feat(integ-tests): Add IntegTestCase (#19829)
Browse files Browse the repository at this point in the history
Adds a new construct, `IntegTestCase`, to allow integration test authors to define tests with which they can control the test execution workflow. In future iterations, it will also allow authors to register live infrastructure assertions.

----

### All Submissions:

* [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/master/CONTRIBUTING.md)

### Adding new Unconventional Dependencies:

* [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/master/CONTRIBUTING.md/#adding-new-unconventional-dependencies)

### New Features

* [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/master/INTEGRATION_TESTS.md)?
	* [ ] Did you use `cdk-integ` to deploy the infrastructure and generate the snapshot (i.e. `cdk-integ` without `--dry-run`)?

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
otaviomacedo committed Apr 12, 2022
1 parent 42e5d08 commit ad249c9
Show file tree
Hide file tree
Showing 17 changed files with 682 additions and 133 deletions.
Original file line number Diff line number Diff line change
@@ -1,17 +1,9 @@
import { DeployOptions, DestroyOptions } from './commands';

/**
* Represents an integration test test case
* The set of options to control the workflow of the test runner
*/
export interface TestCase {
/**
* Stacks that should be tested as part of this test case
* The stackNames will be passed as args to the cdk commands
* so dependent stacks will be automatically deployed unless
* `exclusively` is passed
*/
readonly stacks: string[];

export interface TestOptions {
/**
* Run update workflow on this test case
* This should only be set to false to test scenarios
Expand All @@ -22,54 +14,67 @@ export interface TestCase {
readonly stackUpdateWorkflow?: boolean;

/**
* Additional options to use for each CDK command
*
* @default - runner default options
*/
* Additional options to use for each CDK command
*
* @default - runner default options
*/
readonly cdkCommandOptions?: CdkCommands;

/**
* Additional commands to run at predefined points in the test workflow
*
* e.g. { postDeploy: ['yarn', 'test'] }
*
* @default - no hooks
*/
* Additional commands to run at predefined points in the test workflow
*
* e.g. { postDeploy: ['yarn', 'test'] }
*
* @default - no hooks
*/
readonly hooks?: Hooks;

/**
* Whether or not to include asset hashes in the diff
* Asset hashes can introduces a lot of unneccessary noise into tests,
* but there are some cases where asset hashes _should_ be included. For example
* any tests involving custom resources or bundling
*
* @default false
*/
* Whether or not to include asset hashes in the diff
* Asset hashes can introduces a lot of unneccessary noise into tests,
* but there are some cases where asset hashes _should_ be included. For example
* any tests involving custom resources or bundling
*
* @default false
*/
readonly diffAssets?: boolean;

/**
* List of CloudFormation resource types in this stack that can
* be destroyed as part of an update without failing the test.
*
* This list should only include resources that for this specific
* integration test we are sure will not cause errors or an outage if
* destroyed. For example, maybe we know that a new resource will be created
* first before the old resource is destroyed which prevents any outage.
*
* e.g. ['AWS::IAM::Role']
*
* @default - do not allow destruction of any resources on update
*/
* List of CloudFormation resource types in this stack that can
* be destroyed as part of an update without failing the test.
*
* This list should only include resources that for this specific
* integration test we are sure will not cause errors or an outage if
* destroyed. For example, maybe we know that a new resource will be created
* first before the old resource is destroyed which prevents any outage.
*
* e.g. ['AWS::IAM::Role']
*
* @default - do not allow destruction of any resources on update
*/
readonly allowDestroy?: string[];

/**
* Limit deployment to these regions
*
* @default - can run in any region
*/
* Limit deployment to these regions
*
* @default - can run in any region
*/
readonly regions?: string[];
}

/**
* Represents an integration test case
*/
export interface TestCase extends TestOptions {
/**
* Stacks that should be tested as part of this test case
* The stackNames will be passed as args to the cdk commands
* so dependent stacks will be automatically deployed unless
* `exclusively` is passed
*/
readonly stacks: string[];
}

/**
* Commands to run at predefined points during the
* integration test workflow
Expand Down
93 changes: 3 additions & 90 deletions packages/@aws-cdk/integ-runner/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,98 +146,11 @@ Test Results:
Tests: 1 passed, 1 total
```


### integ.json schema

See [@aws-cdk/cloud-assembly-schema/lib/integ-tests/schema.ts](../cloud-assembly-schema/lib/integ-tests/schema.ts)

### defining an integration test

In most cases an integration test will be an instance of a stack

```ts
import { Function, FunctionOptions } from '../lib';

interface MyIntegTestProps extends StackOptions {
functionProps?: FunctionOptions;
}
class MyIntegTest extends Stack {
constructor(scope: Construct, id: string, props: MyIntegTestProps) {
super(scope, id, props);

new Function(this, 'Handler', {
runtime: Runtime.NODEJS_12_X,
handler: 'index.handler',
code: Code.fromAsset(path.join(__dirname, 'lambda-handler')),
...props.functionProps,
});
}
}
```
### Defining an integration test

You would then use the `IntegTest` construct to create your test cases

```ts
new IntegTeset(app, 'ArmTest', {
stacks: [
new MyIntegTest(app, 'Stack1', {
functionProps: {
architecture: lambda.Architecture.ARM_64,
},
}),
],
diffAssets: true,
update: true,
cdkCommandOptions: {
deploy: {
args: {
requireApproval: RequireApproval.NEVER,
json: true,
},
},
destroy: {
args: {
force: true,
},
},
},
});

new IntegTeset(app, 'AmdTest', {
stacks: [
new MyIntegTest(app, 'Stack2', {
functionProps: {
architecture: lambda.Architecture.X86_64,
},
}),
],
});
```

This will synthesize an `integ.json` file with the following contents

```json
{
"ArmTest": {
"stacks": ["Stack1"],
"diffAssets": true,
"update": true,
"cdkCommands": {
"deploy": {
"args": {
"requireApproval": "never",
"json": true
}
},
"destroy": {
"args": {
"force": true
}
}
}
},
"AmdTest": {
"stacks": ["Stack2"]
}
}
```
See the `@aws-cdk/integ-tests` module for information on how to define
integration tests for the runner to exercise.
3 changes: 3 additions & 0 deletions packages/@aws-cdk/integ-tests/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const baseConfig = require('@aws-cdk/cdk-build-tools/config/eslintrc');
baseConfig.parserOptions.project = __dirname + '/tsconfig.json';
module.exports = baseConfig;
19 changes: 19 additions & 0 deletions packages/@aws-cdk/integ-tests/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
*.js
tsconfig.json
*.js.map
*.d.ts
*.generated.ts
dist
lib/generated/resources.ts
.jsii

.LAST_BUILD
.nyc_output
coverage
nyc.config.js
.LAST_PACKAGE
*.snk
!.eslintrc.js

junit.xml
!jest.config.js
28 changes: 28 additions & 0 deletions packages/@aws-cdk/integ-tests/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Don't include original .ts files when doing `npm pack`
*.ts
!*.d.ts
coverage
.nyc_output
*.tgz

dist
.LAST_PACKAGE
.LAST_BUILD
!*.js

# Include .jsii
!.jsii

*.snk

*.tsbuildinfo

tsconfig.json
.eslintrc.js

# exclude cdk artifacts
**/cdk.out
junit.xml
test/
jest.config.js
!*.lit.ts
Loading

0 comments on commit ad249c9

Please sign in to comment.