diff --git a/packages/@aws-cdk/integ-tests/README.md b/packages/@aws-cdk/integ-tests/README.md index 9d67d10c8bb6e..aaa74d5447641 100644 --- a/packages/@aws-cdk/integ-tests/README.md +++ b/packages/@aws-cdk/integ-tests/README.md @@ -23,7 +23,7 @@ certain handler: ```ts interface StackUnderTestProps extends StackProps { - functionProps?: lambda.FunctionProps; + architecture?: lambda.Architecture; } class StackUnderTest extends Stack { @@ -34,7 +34,7 @@ class StackUnderTest extends Stack { runtime: lambda.Runtime.NODEJS_12_X, handler: 'index.handler', code: lambda.Code.fromAsset(path.join(__dirname, 'lambda-handler')), - ...props.functionProps, + architecture: props.architecture, }); } } @@ -67,7 +67,8 @@ class StackUnderTest extends Stack { const app = new App(); const stack = new Stack(app, 'stack'); -new IntegTestCase(stack, 'DifferentArchitectures', { + +const differentArchsCase = new IntegTestCase(stack, 'DifferentArchitectures', { stacks: [ new StackUnderTest(app, 'Stack1', { architecture: lambda.Architecture.ARM_64, @@ -77,6 +78,13 @@ new IntegTestCase(stack, 'DifferentArchitectures', { }), ], }); + +// There must be exactly one instance of TestCase per file +new IntegTest(app, 'integ-test', { + + // Register as many test cases as you want here + testCases: [differentArchsCase], +}); ``` This is all the instruction you need for the integration test runner to know @@ -90,7 +98,7 @@ const stackUnderTest = new Stack(app, 'StackUnderTest', /* ... */); const stack = new Stack(app, 'stack'); -new IntegTestCase(stack, 'CustomizedDeploymentWorkflow', { +const testCase = new IntegTestCase(stack, 'CustomizedDeploymentWorkflow', { stacks: [stackUnderTest], diffAssets: true, stackUpdateWorkflow: true, @@ -108,5 +116,9 @@ new IntegTestCase(stack, 'CustomizedDeploymentWorkflow', { }, }, }); + +new IntegTest(app, 'integ-test', { + testCases: [testCase], +}); ``` diff --git a/packages/@aws-cdk/integ-tests/lib/manifest-synthesizer.ts b/packages/@aws-cdk/integ-tests/lib/manifest-synthesizer.ts new file mode 100644 index 0000000000000..36230a8b7ad9f --- /dev/null +++ b/packages/@aws-cdk/integ-tests/lib/manifest-synthesizer.ts @@ -0,0 +1,30 @@ +import { IntegManifest, Manifest } from '@aws-cdk/cloud-assembly-schema'; +import { ISynthesisSession } from '@aws-cdk/core'; +import { IntegManifestWriter } from './manifest-writer'; +import { IntegTestCase } from './test-case'; + +const emptyManifest: IntegManifest = { + version: '', + testCases: { }, +}; + +export class IntegManifestSynthesizer { + constructor(private readonly testCases: IntegTestCase[]) {} + + synthesize(session: ISynthesisSession) { + const manifest = this.testCases + .map(tc => tc.manifest) + .reduce(mergeManifests, emptyManifest); + + const snapshotDir = session.assembly.outdir; + + IntegManifestWriter.write(manifest, snapshotDir); + } +} + +function mergeManifests(m1: IntegManifest, m2: IntegManifest): IntegManifest { + return { + version: Manifest.version(), + testCases: { ...m1.testCases, ...m2.testCases }, + }; +} diff --git a/packages/@aws-cdk/integ-tests/lib/test-case.ts b/packages/@aws-cdk/integ-tests/lib/test-case.ts index b57fe9ce03643..2c0c6582a413c 100644 --- a/packages/@aws-cdk/integ-tests/lib/test-case.ts +++ b/packages/@aws-cdk/integ-tests/lib/test-case.ts @@ -1,6 +1,6 @@ -import { IntegManifest, TestCase, TestOptions } from '@aws-cdk/cloud-assembly-schema'; -import { attachCustomSynthesis, ISynthesisSession, Stack } from '@aws-cdk/core'; -import { IntegManifestWriter } from './manifest-writer'; +import { IntegManifest, Manifest, TestCase, TestOptions } from '@aws-cdk/cloud-assembly-schema'; +import { attachCustomSynthesis, Stack, ISynthesisSession } from '@aws-cdk/core'; +import { IntegManifestSynthesizer } from './manifest-synthesizer'; // keep this import separate from other imports to reduce chance for merge conflicts with v2-main // eslint-disable-next-line no-duplicate-imports, import/order @@ -21,18 +21,46 @@ export interface IntegTestCaseProps extends TestOptions { * apply to all stacks under this case. */ export class IntegTestCase extends Construct { - constructor(scope: Construct, id: string, props: IntegTestCaseProps) { + constructor(scope: Construct, private readonly id: string, private readonly props: IntegTestCaseProps) { super(scope, id); + } + + /** + * The integration test manifest for this test case. Manifests are used + * by the integration test runner. + */ + get manifest(): IntegManifest { + return { + version: Manifest.version(), + testCases: { [this.id]: toTestCase(this.props) }, + }; + } +} + +/** + * Integration test properties + */ +export interface IntegTestProps { + /** + * List of test cases that make up this test + */ + readonly testCases: IntegTestCase[]; +} +/** + * A collection of test cases. Each test case file should contain exactly one + * instance of this class. + */ +export class IntegTest extends Construct { + constructor(scope: Construct, id: string, private readonly props: IntegTestProps) { + super(scope, id); + } + + protected onPrepare(): void { attachCustomSynthesis(this, { onSynthesize: (session: ISynthesisSession) => { - const snapshotDir = session.assembly.outdir; - const manifest: IntegManifest = { - version: '', - testCases: { [id]: toTestCase(props) }, - }; - - IntegManifestWriter.write(manifest, snapshotDir); + const synthesizer = new IntegManifestSynthesizer(this.props.testCases); + synthesizer.synthesize(session); }, }); } diff --git a/packages/@aws-cdk/integ-tests/package.json b/packages/@aws-cdk/integ-tests/package.json index e82bbad41516c..813925be51265 100644 --- a/packages/@aws-cdk/integ-tests/package.json +++ b/packages/@aws-cdk/integ-tests/package.json @@ -70,6 +70,7 @@ "dependencies": { "@aws-cdk/cloud-assembly-schema": "0.0.0", "@aws-cdk/core": "0.0.0", + "@aws-cdk/cx-api": "0.0.0", "constructs": "^3.3.69" }, "repository": { diff --git a/packages/@aws-cdk/integ-tests/rosetta/default.ts-fixture b/packages/@aws-cdk/integ-tests/rosetta/default.ts-fixture index 83fe423064206..648e54426b3e4 100644 --- a/packages/@aws-cdk/integ-tests/rosetta/default.ts-fixture +++ b/packages/@aws-cdk/integ-tests/rosetta/default.ts-fixture @@ -1,5 +1,5 @@ import * as lambda from '@aws-cdk/aws-lambda'; -import { IntegTestCase } from '@aws-cdk/integ-tests'; +import { IntegTestCase, IntegTest } from '@aws-cdk/integ-tests'; import { App, Construct, diff --git a/packages/@aws-cdk/integ-tests/test/manifest-synthesizer.test.ts b/packages/@aws-cdk/integ-tests/test/manifest-synthesizer.test.ts new file mode 100644 index 0000000000000..03ee7691a6802 --- /dev/null +++ b/packages/@aws-cdk/integ-tests/test/manifest-synthesizer.test.ts @@ -0,0 +1,45 @@ +import * as fs from 'fs'; +import * as os from 'os'; +import * as path from 'path'; +import { App, Stack } from '@aws-cdk/core'; +import { CloudAssemblyBuilder } from '@aws-cdk/cx-api'; +import { IntegTestCase } from '../lib'; +import { IntegManifestSynthesizer } from '../lib/manifest-synthesizer'; +import { IntegManifestWriter } from '../lib/manifest-writer'; + +describe(IntegManifestSynthesizer, () => { + it('synthesizes a multiple manifests', () => { + const write = jest.spyOn(IntegManifestWriter, 'write'); + + const app = new App(); + const stack = new Stack(app, 'stack'); + const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cdk-test')); + const assembly = new CloudAssemblyBuilder(tmpDir); + + const synthesizer = new IntegManifestSynthesizer([ + new IntegTestCase(stack, 'case1', { + stacks: [new Stack(app, 'stack-under-test-1')], + }), + new IntegTestCase(stack, 'case2', { + stacks: [new Stack(app, 'stack-under-test-2')], + }), + ]); + + synthesizer.synthesize({ + assembly, + outdir: 'asdas', + }); + + expect(write).toHaveBeenCalledWith({ + version: '17.0.0', + testCases: { + case1: { + stacks: ['stack-under-test-1'], + }, + case2: { + stacks: ['stack-under-test-2'], + }, + }, + }, tmpDir); + }); +}); \ No newline at end of file