-
Notifications
You must be signed in to change notification settings - Fork 4k
/
Copy pathtest-case.ts
190 lines (166 loc) · 5.46 KB
/
test-case.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
import { IntegManifest, Manifest, TestCase, TestOptions } from 'aws-cdk-lib/cloud-assembly-schema';
import { attachCustomSynthesis, ISynthesisSession, Stack, StackProps } from 'aws-cdk-lib/core';
import { Construct } from 'constructs';
import { IDeployAssert } from './assertions';
import { DeployAssert } from './assertions/private/deploy-assert';
import { IntegManifestSynthesizer } from './manifest-synthesizer';
const TEST_CASE_STACK_SYMBOL = Symbol.for('@aws-cdk/integ-tests.IntegTestCaseStack');
/**
* Properties of an integration test case
*/
export interface IntegTestCaseProps extends TestOptions {
/**
* Stacks to be deployed during the test
*/
readonly stacks: Stack[];
/**
* Specify a stack to use for assertions
*
* @default - a stack is created for you
*/
readonly assertionStack?: Stack;
}
/**
* An integration test case. Allows the definition of test properties that
* apply to all stacks under this case.
*
* It is recommended that you use the IntegTest construct since that will create
* a default IntegTestCase
*/
export class IntegTestCase extends Construct {
/**
* Make assertions on resources in this test case
*/
public readonly assertions: IDeployAssert;
private readonly _assert: DeployAssert;
constructor(scope: Construct, id: string, private readonly props: IntegTestCaseProps) {
super(scope, id);
this._assert = new DeployAssert(this, { stack: props.assertionStack });
this.assertions = this._assert;
}
/**
* 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.node.path]: this.toTestCase(this.props) },
};
}
private toTestCase(props: IntegTestCaseProps): TestCase {
return {
...props,
assertionStack: this._assert.scope.node.path,
assertionStackName: this._assert.scope.stackName,
stacks: props.stacks.map(s => s.node.path),
};
}
}
/**
* Properties of an integration test case stack
*/
export interface IntegTestCaseStackProps extends TestOptions, StackProps { }
/**
* An integration test case stack. Allows the definition of test properties
* that should apply to this stack.
*
* This should be used if there are multiple stacks in the integration test
* and it is necessary to specify different test case option for each. Otherwise
* normal stacks should be added to IntegTest
*/
export class IntegTestCaseStack extends Stack {
/**
* Returns whether the construct is a IntegTestCaseStack
*/
public static isIntegTestCaseStack(x: any): x is IntegTestCaseStack {
return x !== null && typeof (x) === 'object' && TEST_CASE_STACK_SYMBOL in x;
}
/**
* Make assertions on resources in this test case
*/
public readonly assertions: IDeployAssert;
/**
* The underlying IntegTestCase that is created
* @internal
*/
public readonly _testCase: IntegTestCase;
constructor(scope: Construct, id: string, props?: IntegTestCaseStackProps) {
super(scope, id, props);
Object.defineProperty(this, TEST_CASE_STACK_SYMBOL, { value: true });
// TODO: should we only have a single DeployAssert per test?
this.assertions = new DeployAssert(this);
this._testCase = new IntegTestCase(this, `${id}TestCase`, {
...props,
stacks: [this],
});
}
}
/**
* Integration test properties
*/
export interface IntegTestProps extends TestOptions {
/**
* List of test cases that make up this test
*/
readonly testCases: Stack[];
/**
* Enable lookups for this test. If lookups are enabled
* then `stackUpdateWorkflow` must be set to false.
* Lookups should only be enabled when you are explicitly testing
* lookups.
*
* @default false
*/
readonly enableLookups?: boolean;
/**
* Specify a stack to use for assertions
*
* @default - a stack is created for you
*/
readonly assertionStack?: Stack;
}
/**
* A collection of test cases. Each test case file should contain exactly one
* instance of this class.
*/
export class IntegTest extends Construct {
/**
* Make assertions on resources in this test case
*/
public readonly assertions: IDeployAssert;
private readonly testCases: IntegTestCase[];
private readonly enableLookups?: boolean;
constructor(scope: Construct, id: string, props: IntegTestProps) {
super(scope, id);
this.enableLookups = props.enableLookups;
const defaultTestCase = new IntegTestCase(this, 'DefaultTest', {
stacks: props.testCases.filter(stack => !IntegTestCaseStack.isIntegTestCaseStack(stack)),
hooks: props.hooks,
regions: props.regions,
diffAssets: props.diffAssets,
allowDestroy: props.allowDestroy,
cdkCommandOptions: props.cdkCommandOptions,
stackUpdateWorkflow: props.stackUpdateWorkflow,
assertionStack: props.assertionStack,
});
this.assertions = defaultTestCase.assertions;
this.testCases = [
defaultTestCase,
...props.testCases
.filter(stack => IntegTestCaseStack.isIntegTestCaseStack(stack))
.map(stack => (stack as IntegTestCaseStack)._testCase),
];
this.node.addValidation({
validate: () => {
attachCustomSynthesis(this, {
onSynthesize: (session: ISynthesisSession) => {
const synthesizer = new IntegManifestSynthesizer(this.testCases, this.enableLookups);
synthesizer.synthesize(session);
},
});
return [];
},
});
}
}