Skip to content

Commit 0f3d331

Browse files
authored
fix(integ-tests): DeployAssert should be private (#20382)
The `DeployAssert` construct is really an implementation detail that is only used when making assertions as part of integration tests. This PR makes the construct private and creates a public interface (`IDeployAssert`). This PR also: - Removes the scope swap since we no longer need to pass around `DeployAssert`. - Removes some unused code (ResultsCollector). ---- ### All Submissions: * [ ] 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 `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn 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*
1 parent c50d60c commit 0f3d331

File tree

14 files changed

+306
-327
lines changed

14 files changed

+306
-327
lines changed

packages/@aws-cdk/integ-tests/README.md

+42-34
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,12 @@ new IntegTest(app, 'Integ', { testCases: [stackUnderTest, testCaseWithAssets] })
177177

178178
This library also provides a utility to make assertions against the infrastructure that the integration test deploys.
179179

180-
The easiest way to do this is to create a `TestCase` and then access the `DeployAssert` that is automatically created.
180+
There are two main scenarios in which assertions are created.
181+
182+
- Part of an integration test using `integ-runner`
183+
184+
In this case you would create an integration test using the `IntegTest` construct and then make assertions using the `assert` property.
185+
You should **not** utilize the assertion constructs directly, but should instead use the `methods` on `IntegTest.assert`.
181186

182187
```ts
183188
declare const app: App;
@@ -187,31 +192,35 @@ const integ = new IntegTest(app, 'Integ', { testCases: [stack] });
187192
integ.assert.awsApiCall('S3', 'getObject');
188193
```
189194

190-
### DeployAssert
191-
192-
Assertions are created by using the `DeployAssert` construct. This construct creates it's own `Stack` separate from
193-
any stacks that you create as part of your integration tests. This `Stack` is treated differently from other stacks
194-
by the `integ-runner` tool. For example, this stack will not be diffed by the `integ-runner`.
195+
- Part of a normal CDK deployment
195196

196-
Any assertions that you create should be created in the scope of `DeployAssert`. For example,
197+
In this case you may be using assertions as part of a normal CDK deployment in order to make an assertion on the infrastructure
198+
before the deployment is considered successful. In this case you can utilize the assertions constructs directly.
197199

198200
```ts
199-
declare const app: App;
201+
declare const myAppStack: Stack;
200202

201-
const assert = new DeployAssert(app);
202-
new AwsApiCall(assert, 'GetObject', {
203+
new AwsApiCall(myAppStack, 'GetObject', {
203204
service: 'S3',
204205
api: 'getObject',
205206
});
206207
```
207208

209+
### DeployAssert
210+
211+
Assertions are created by using the `DeployAssert` construct. This construct creates it's own `Stack` separate from
212+
any stacks that you create as part of your integration tests. This `Stack` is treated differently from other stacks
213+
by the `integ-runner` tool. For example, this stack will not be diffed by the `integ-runner`.
214+
208215
`DeployAssert` also provides utilities to register your own assertions.
209216

210217
```ts
211218
declare const myCustomResource: CustomResource;
219+
declare const stack: Stack;
212220
declare const app: App;
213-
const assert = new DeployAssert(app);
214-
assert.assert(
221+
222+
const integ = new IntegTest(app, 'Integ', { testCases: [stack] });
223+
integ.assert.assert(
215224
'CustomAssertion',
216225
ExpectedResult.objectLike({ foo: 'bar' }),
217226
ActualResult.fromCustomResource(myCustomResource, 'data'),
@@ -228,12 +237,12 @@ AWS API call to receive some data. This library does this by utilizing CloudForm
228237
which means that CloudFormation will call out to a Lambda Function which will
229238
use the AWS JavaScript SDK to make the API call.
230239

231-
This can be done by using the class directory:
240+
This can be done by using the class directory (in the case of a normal deployment):
232241

233242
```ts
234-
declare const assert: DeployAssert;
243+
declare const stack: Stack;
235244

236-
new AwsApiCall(assert, 'MyAssertion', {
245+
new AwsApiCall(stack, 'MyAssertion', {
237246
service: 'SQS',
238247
api: 'receiveMessage',
239248
parameters: {
@@ -242,12 +251,15 @@ new AwsApiCall(assert, 'MyAssertion', {
242251
});
243252
```
244253

245-
Or by using the `awsApiCall` method on `DeployAssert`:
254+
Or by using the `awsApiCall` method on `DeployAssert` (when writing integration tests):
246255

247256
```ts
248257
declare const app: App;
249-
const assert = new DeployAssert(app);
250-
assert.awsApiCall('SQS', 'receiveMessage', {
258+
declare const stack: Stack;
259+
const integ = new IntegTest(app, 'Integ', {
260+
testCases: [stack],
261+
});
262+
integ.assert.awsApiCall('SQS', 'receiveMessage', {
251263
QueueUrl: 'url',
252264
});
253265
```
@@ -281,21 +293,18 @@ const message = integ.assert.awsApiCall('SQS', 'receiveMessage', {
281293
WaitTimeSeconds: 20,
282294
});
283295

284-
new EqualsAssertion(integ.assert, 'ReceiveMessage', {
285-
actual: ActualResult.fromAwsApiCall(message, 'Messages.0.Body'),
286-
expected: ExpectedResult.objectLike({
287-
requestContext: {
288-
condition: 'Success',
289-
},
290-
requestPayload: {
291-
status: 'OK',
292-
},
293-
responseContext: {
294-
statusCode: 200,
295-
},
296-
responsePayload: 'success',
297-
}),
298-
});
296+
message.assertAtPath('Messages.0.Body', ExpectedResult.objectLike({
297+
requestContext: {
298+
condition: 'Success',
299+
},
300+
requestPayload: {
301+
status: 'OK',
302+
},
303+
responseContext: {
304+
statusCode: 200,
305+
},
306+
responsePayload: 'success',
307+
}));
299308
```
300309

301310
#### Match
@@ -305,7 +314,6 @@ can be used to construct the `ExpectedResult`.
305314

306315
```ts
307316
declare const message: AwsApiCall;
308-
declare const assert: DeployAssert;
309317

310318
message.assert(ExpectedResult.objectLike({
311319
Messages: Match.arrayWith([

packages/@aws-cdk/integ-tests/lib/assertions/common.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { CustomResource } from '@aws-cdk/core';
2-
import { AwsApiCall } from './sdk';
2+
import { IAwsApiCall } from './sdk';
3+
34
/**
45
* Represents the "actual" results to compare
56
*/
@@ -16,7 +17,7 @@ export abstract class ActualResult {
1617
/**
1718
* Get the actual results from a AwsApiCall
1819
*/
19-
public static fromAwsApiCall(query: AwsApiCall, attribute: string): ActualResult {
20+
public static fromAwsApiCall(query: IAwsApiCall, attribute: string): ActualResult {
2021
return {
2122
result: query.getAttString(attribute),
2223
};

packages/@aws-cdk/integ-tests/lib/assertions/deploy-assert.ts

-128
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
export * from './assertions';
1+
export * from './types';
22
export * from './sdk';
3-
export * from './deploy-assert';
3+
export * from './assertions';
44
export * from './providers';
55
export * from './common';
66
export * from './match';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { Stack } from '@aws-cdk/core';
2+
import { Construct, IConstruct, Node } from 'constructs';
3+
import { EqualsAssertion } from '../assertions';
4+
import { ExpectedResult, ActualResult } from '../common';
5+
import { md5hash } from '../private/hash';
6+
import { AwsApiCall, LambdaInvokeFunction, IAwsApiCall, LambdaInvokeFunctionProps } from '../sdk';
7+
import { IDeployAssert } from '../types';
8+
9+
10+
const DEPLOY_ASSERT_SYMBOL = Symbol.for('@aws-cdk/integ-tests.DeployAssert');
11+
12+
13+
// keep this import separate from other imports to reduce chance for merge conflicts with v2-main
14+
// eslint-disable-next-line no-duplicate-imports, import/order
15+
import { Construct as CoreConstruct } from '@aws-cdk/core';
16+
17+
/**
18+
* Options for DeployAssert
19+
*/
20+
export interface DeployAssertProps { }
21+
22+
/**
23+
* Construct that allows for registering a list of assertions
24+
* that should be performed on a construct
25+
*/
26+
export class DeployAssert extends CoreConstruct implements IDeployAssert {
27+
28+
/**
29+
* Returns whether the construct is a DeployAssert construct
30+
*/
31+
public static isDeployAssert(x: any): x is DeployAssert {
32+
return x !== null && typeof(x) === 'object' && DEPLOY_ASSERT_SYMBOL in x;
33+
}
34+
35+
/**
36+
* Finds a DeployAssert construct in the given scope
37+
*/
38+
public static of(construct: IConstruct): DeployAssert {
39+
const scopes = Node.of(Node.of(construct).root).findAll();
40+
const deployAssert = scopes.find(s => DeployAssert.isDeployAssert(s));
41+
if (!deployAssert) {
42+
throw new Error('No DeployAssert construct found in scopes');
43+
}
44+
return deployAssert as DeployAssert;
45+
}
46+
47+
public scope: Stack;
48+
49+
constructor(scope: Construct) {
50+
super(scope, 'Default');
51+
52+
this.scope = new Stack(scope, 'DeployAssert');
53+
54+
Object.defineProperty(this, DEPLOY_ASSERT_SYMBOL, { value: true });
55+
}
56+
57+
public awsApiCall(service: string, api: string, parameters?: any): IAwsApiCall {
58+
return new AwsApiCall(this.scope, `AwsApiCall${service}${api}`, {
59+
api,
60+
service,
61+
parameters,
62+
});
63+
}
64+
65+
public invokeFunction(props: LambdaInvokeFunctionProps): IAwsApiCall {
66+
const hash = md5hash(this.scope.resolve(props));
67+
return new LambdaInvokeFunction(this.scope, `LambdaInvoke${hash}`, props);
68+
}
69+
70+
public assert(id: string, expected: ExpectedResult, actual: ActualResult): void {
71+
new EqualsAssertion(this.scope, `EqualsAssertion${id}`, {
72+
expected,
73+
actual,
74+
});
75+
}
76+
}

packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/index.ts

-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { AssertionHandler } from './assertion';
2-
import { ResultsCollectionHandler } from './results';
32
import { AwsApiCallHandler } from './sdk';
43
import * as types from './types';
54

@@ -14,7 +13,6 @@ function createResourceHandler(event: AWSLambda.CloudFormationCustomResourceEven
1413
}
1514
switch (event.ResourceType) {
1615
case types.ASSERT_RESOURCE_TYPE: return new AssertionHandler(event, context);
17-
case types.RESULTS_RESOURCE_TYPE: return new ResultsCollectionHandler(event, context);
1816
default:
1917
throw new Error(`Unsupported resource type "${event.ResourceType}`);
2018
}

packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/results.ts

-12
This file was deleted.

0 commit comments

Comments
 (0)