Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(integ-tests): enhancements to integ-tests #20180

Merged
merged 12 commits into from
May 16, 2022
Merged

feat(integ-tests): enhancements to integ-tests #20180

merged 12 commits into from
May 16, 2022

Conversation

corymhall
Copy link
Contributor

@corymhall corymhall commented May 2, 2022

This PR contains various enhancements including

  • integ-tests

    • removed dependency on other CDK libraries (other than core)
    • API ergonomics improvements
      • renamed queryAws to awsApiCall
      • added some additional methods
    • Now using Match from @aws-cdk/assertions for the assertions provider
    • DeployAssert now creates its own stack
      • This stack is written to a new IntegManifest property so that it can be treated differently
        (i.e. don't diff this stack)
    • Additional assertion types (OBJECT_LIKE)
    • Refactored assertion results
      • removed separate results handler in favor of just writing results to a stack output
    • utility for invoking lambda functions (separate from awsApiCall)
    • IntegTest now creates a test case by default.
    • Added IntegTestCaseStack class
  • integ-runner

    • Updated to handle the results of assertions
      • When running with update workflow, the assertion stack is only deployed during the
        "update" deployment
      • The stack outputs containing the assertion results are are written to a
        file that the runner can read.

I've also converted/added assertions to a couple of existing integration tests

  • aws-lambda/test/integ.bundling.ts
  • aws-lambda-destinations/test/integ.destinations.ts
  • aws-stepfunctions-tasks/test/eventbridge/integ.put-events.ts

All Submissions:

Adding new Unconventional Dependencies:

  • This PR adds new unconventional dependencies following the process described here

New Features

  • Have you added the new feature to an integration test?
    • 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

This PR contains various enhancements including

- API ergonomics improvements
  - renamed `queryAws` to `awsApiCall`
- Now using `Match` from @aws-cdk/assertions for the assertions provider
- `DeployAssert` now creates its own stack
- Additional assertion types (OBJECT_LIKE)
- utility for invoking lambda functions (separate from `awsApiCall`)
@gitpod-io
Copy link

gitpod-io bot commented May 2, 2022

@corymhall corymhall requested a review from rix0rrr May 2, 2022 20:32
@aws-cdk-automation aws-cdk-automation requested a review from a team May 2, 2022 20:32
@github-actions github-actions bot added the p2 label May 2, 2022
@mergify mergify bot added the contribution/core This is a PR that came from AWS. label May 2, 2022
@corymhall corymhall marked this pull request as ready for review May 3, 2022 11:48
@corymhall corymhall added the pr-linter/exempt-readme The PR linter will not require README changes label May 3, 2022
},
responsePayload: 'success',
}),
assertionType: AssertionType.OBJECT_LIKE,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we not do ExpectedResult.objectLike() instead of this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Combining what you said in the previous comment maybe I'll add another method so you could do

message.assertObjectLikeAtPath('Messages.0.Body', {
  ...,
});

Or we could reduce the methods down and put the type of assertion on ExpectedResult? Something like

message.assert(ExpectedResult.objectLike());
message.assert(ExpectedResult.objectEquals());
message.assertAtPath('Messages.0.Body', ExpectedResult.stringEquals());

I think ideally you would not need to use the EqualsAssertion class directly.

WaitTimeSeconds: 20,
});

new EqualsAssertion(integ.assert, 'ReceiveMessage', {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Helper function on message for this?

message.assertEquals(ExpectedResult.fromObject({
  // ...
}));

?

Or if we need to select a subobject:

message.atPath('Message.0.Body')

});

// assert the results
describe.assertObjectLike({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh shit that exists 😅

@@ -238,26 +245,79 @@ export class IntegTestRunner extends IntegRunner {
lookups: this.expectedTestSuite?.enableLookups,
});
}
// now deploy the "actual" test. If there are any assertions
// deploy the assertion stack as well
this.cdk.deploy({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this not async?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not, it uses spawnSync.

* Process the outputsFile which contains the assertions results as stack
* outputs
*/
private processAssertionResults(file: string, assertionStackId: string): AssertionResults | undefined {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a need for this to return undefined? Can we not just return an empty object?

Motivation: I like avoiding ifs and special cases as much as I can.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to check if the results have any assertion failures so I can fail the test and report on the failures. I would either need to check for undefined or an empty object and undefined seemed better. I can be persuaded otherwise if you think it is not though.

packages/@aws-cdk/integ-tests/README.md Outdated Show resolved Hide resolved
Comment on lines +400 to +402
"Export": {
"Name": "aws-cdk-lambda-destinations:ExportsOutputRefSnsSqsC4810B27404A5AFF"
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is going to prevent replacements. Feels a little scary, is that definitely what we want?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think so? I think in a lot of cases we would want to prevent replacements as that could be a breaking change. If it was really necessary then you could run without the update workflow. I'm not sure what the alternative would be that wouldn't have it's own tradeoffs.

let result: AssertionResult;
let matcher;
console.log(`Testing equality between ${JSON.stringify(request.actual)} and ${JSON.stringify(request.expected)}`);

switch (request.assertionType) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of having a single assertionType, could we do something arbitrarily recursive? We could encode it the same way as CloudFormation intrinsics: Something like:

{ '$ObjectLike': {
  Message: [
    Body: { 
      'Match::Like': {
        Elements: { '$ArrayWith': [{ Asdf: 3 }] },
      }
    }
  ]
}}

Then in the handler we'd recursively turn that structure into matchers and arguments to matchers before executing.

On the construction side, we'd have helpers to construct this JSON so consumers don't have to see the ugliness:

export class Match {
  public static arrayWith(xs: string[]): any {
    return { '$ArrayWith': xs };
  }
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm that is an interesting idea

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added. Let me know what you think about the API.

Comment on lines +37 to +41
status: 'fail',
message: [
...matchResult.toHumanStrings(),
JSON.stringify(matchResult.target, undefined, 2),
].join('\n'),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we choose to make the matcher succeed even if the assertion fails, do we also have a resource to make the deployment fail based on the outputs of all registered assertions? That will be necessary if we want to run assertions without the integ runner (like in CDK pipelines).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could just have a prop that the user could pass that would cause the custom resource to fail if the assertion fails.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a prop, let me know what you think.

@corymhall corymhall requested a review from rix0rrr May 4, 2022 19:50
Comment on lines +126 to +131
case '$ArrayWith':
return Match.arrayWith(v[nested]);
case '$ObjectLike':
return Match.objectLike(v[nested]);
case '$StringLike':
return Match.stringLikeRegexp(v[nested]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this case statement duplicated in the constructor? We shouldn't need both.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that is true. Updated to not duplicate this in the constructor.

*/
public getMatcher(): Matcher {
try {
const final = JSON.parse(JSON.stringify(this.parsedObj), function(_k, v) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's an interesting way of doing it. I would have expected an explicit recursion but I suppose this will work

Comment on lines +46 to +47
scope = new Stack(scope, 'DeployAssert');
super(scope, 'Default');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is weird to me. Why is the Stack not inside the DeployAssert ?

We can hold on to the stack in a private readonly scope so that we can still create other resources inside the right Stack. But the swapparoo here is a little confusing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's so that we don't have to create DeployAssert in the scope of a stack. I was originally going to make DeployAssert extend Stack but I thought it would be nice if DeployAssert could be treated like a stack, but the user wouldn't be able to access any of the stack functionality (the methods they would see would just be the DeployAssert methods and not any Stack` methods).

So you could create any of the assertion resources by passing in DeployAssert as the scope

const deployAssert = new DeployAssert(app);
new AwsApiCall(deployAssert, 'AwsApiCall', {...});

We could change it to what you suggested and make it a public readonly stack which the user would have to know to use

const deployAssert = new DeployAssert(app);
new AwsApiCall(deployAssert.stack, 'AwsApiCall', {...});

*/
readonly expected: any;
readonly reportFailure?: boolean;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

failDeployment ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

}),
};
if (request.reportFailure) {
throw new Error(result.data);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this throw going to be shown with the error message in the stack deployment? Do we run this code through the custom resource provider?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes this throw will be shown in the error message in the stack deployment. The throw is caught in the provider and returned in the response to CloudFormation.

@corymhall corymhall requested a review from rix0rrr May 16, 2022 14:09
@rix0rrr rix0rrr added the pr/do-not-merge This PR should not be merged at this time. label May 16, 2022
Copy link
Contributor

@rix0rrr rix0rrr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved modulo a comment explaining the stack swapparoo

@corymhall corymhall removed the pr/do-not-merge This PR should not be merged at this time. label May 16, 2022
@mergify
Copy link
Contributor

mergify bot commented May 16, 2022

Thank you for contributing! Your pull request will be updated from master and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork).

@aws-cdk-automation
Copy link
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: AutoBuildProject89A8053A-LhjRyN9kxr8o
  • Commit ID: 1295a69
  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@mergify mergify bot merged commit 3ff3fb7 into aws:master May 16, 2022
@mergify
Copy link
Contributor

mergify bot commented May 16, 2022

Thank you for contributing! Your pull request will be updated from master and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork).

wphilipw pushed a commit to wphilipw/aws-cdk that referenced this pull request May 23, 2022
This PR contains various enhancements including

- `integ-tests`
    - removed dependency on other CDK libraries (other than core)
	- API ergonomics improvements
 		- renamed `queryAws` to `awsApiCall`
 		- added some additional methods
	- Now using `Match` from @aws-cdk/assertions for the assertions provider
	- `DeployAssert` now creates its own stack
		- This stack is written to a new IntegManifest property so that it can be treated differently
			(i.e. don't diff this stack)
	- Additional assertion types (OBJECT_LIKE)
	- Refactored assertion results
		- removed separate results handler in favor of just writing results to a stack output
	- utility for invoking lambda functions (separate from `awsApiCall`)
	- `IntegTest` now creates a test case by default.
	- Added `IntegTestCaseStack` class

- `integ-runner`
	- Updated to handle the results of assertions
		- When running with update workflow, the assertion stack is only deployed during the 
			"update" deployment
		- The stack outputs containing the assertion results are are written to a
			file that the runner can read.



I've also converted/added assertions to a couple of existing integration tests
- `aws-lambda/test/integ.bundling.ts`
- `aws-lambda-destinations/test/integ.destinations.ts`
- `aws-stepfunctions-tasks/test/eventbridge/integ.put-events.ts`

----

### 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*
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
contribution/core This is a PR that came from AWS. p2 pr-linter/exempt-readme The PR linter will not require README changes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants