Skip to content

Commit

Permalink
feat(assertions): Add the hasNoXXX methods. (#19330)
Browse files Browse the repository at this point in the history
This PR adds `hasNoError`, `hasNoWarning`, and `hasNoInfo` methods to the Annotations assertions toolkit. 

Fixes #18874

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
joe-king-sh authored Mar 15, 2022
1 parent 1bdec59 commit 6bdc9eb
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 5 deletions.
6 changes: 3 additions & 3 deletions packages/@aws-cdk/assertions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -550,9 +550,9 @@ Annotations.fromStack(stack).hasError(

Here are the available APIs for `Annotations`:

- `hasError()` and `findError()`
- `hasWarning()` and `findWarning()`
- `hasInfo()` and `findInfo()`
- `hasError()`, `hasNoError()`, and `findError()`
- `hasWarning()`, `hasNoWarning()`, and `findWarning()`
- `hasInfo()`, `hasNoInfo()`, and `findInfo()`

The corresponding `findXxx()` API is complementary to the `hasXxx()` API, except instead
of asserting its presence, it returns the set of matching messages.
Expand Down
41 changes: 40 additions & 1 deletion packages/@aws-cdk/assertions/lib/annotations.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Stack, Stage } from '@aws-cdk/core';
import { SynthesisMessage } from '@aws-cdk/cx-api';
import { Messages } from './private/message';
import { findMessage, hasMessage } from './private/messages';
import { findMessage, hasMessage, hasNoMessage } from './private/messages';

/**
* Suite of assertions that can be run on a CDK Stack.
Expand Down Expand Up @@ -35,6 +35,19 @@ export class Annotations {
}
}

/**
* Assert that an error with the given message does not exist in the synthesized CDK `Stack`.
*
* @param constructPath the construct path to the error. Provide `'*'` to match all errors in the template.
* @param message the error message as should be expected. This should be a string or Matcher object.
*/
public hasNoError(constructPath: string, message: any): void {
const matchError = hasNoMessage(this._messages, constructPath, constructMessage('error', message));
if (matchError) {
throw new Error(matchError);
}
}

/**
* Get the set of matching errors of a given construct path and message.
*
Expand All @@ -58,6 +71,19 @@ export class Annotations {
}
}

/**
* Assert that an warning with the given message does not exist in the synthesized CDK `Stack`.
*
* @param constructPath the construct path to the warning. Provide `'*'` to match all warnings in the template.
* @param message the warning message as should be expected. This should be a string or Matcher object.
*/
public hasNoWarning(constructPath: string, message: any): void {
const matchError = hasNoMessage(this._messages, constructPath, constructMessage('warning', message));
if (matchError) {
throw new Error(matchError);
}
}

/**
* Get the set of matching warning of a given construct path and message.
*
Expand All @@ -81,6 +107,19 @@ export class Annotations {
}
}

/**
* Assert that an info with the given message does not exist in the synthesized CDK `Stack`.
*
* @param constructPath the construct path to the info. Provide `'*'` to match all info in the template.
* @param message the info message as should be expected. This should be a string or Matcher object.
*/
public hasNoInfo(constructPath: string, message: any): void {
const matchError = hasNoMessage(this._messages, constructPath, constructMessage('info', message));
if (matchError) {
throw new Error(matchError);
}
}

/**
* Get the set of matching infos of a given construct path and message.
*
Expand Down
16 changes: 15 additions & 1 deletion packages/@aws-cdk/assertions/lib/private/messages.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { SynthesisMessage } from '@aws-cdk/cx-api';
import { Messages } from './message';
import { formatFailure, matchSection } from './section';
import { formatAllMatches, formatFailure, matchSection } from './section';

export function findMessage(messages: Messages, constructPath: string, props: any = {}): { [key: string]: { [key: string]: any } } {
const section: { [key: string]: SynthesisMessage } = messages;
Expand Down Expand Up @@ -32,6 +32,20 @@ export function hasMessage(messages: Messages, constructPath: string, props: any
].join('\n');
}

export function hasNoMessage(messages: Messages, constructPath: string, props: any): string | void {
const section: { [key: string]: SynthesisMessage } = messages;
const result = matchSection(filterPath(section, constructPath), props);

if (!result.match) {
return;
}

return [
`Expected no matches, but stack has ${Object.keys(result.matches).length} messages as follows:`,
formatAllMatches(result.matches),
].join('\n');
}

// We redact the stack trace by default because it is unnecessarily long and unintelligible.
// If there is a use case for rendering the trace, we can add it later.
function handleTrace(match: any, redact: boolean = true): void {
Expand Down
6 changes: 6 additions & 0 deletions packages/@aws-cdk/assertions/lib/private/section.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ function eachEntryInSection(
}
}

export function formatAllMatches(matches: {[key: string]: any}): string {
return [
leftPad(JSON.stringify(matches, undefined, 2)),
].join('\n');
}

export function formatFailure(closestResult: MatchResult): string {
return [
'The closest result is:',
Expand Down
33 changes: 33 additions & 0 deletions packages/@aws-cdk/assertions/test/annotations.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,17 @@ describe('Messages', () => {
});
});

describe('hasNoError', () => {
test('match', () => {
annotations.hasNoError('/Default/Fred', Match.anyValue());
});

test('no match', () => {
expect(() => annotations.hasNoError('/Default/Foo', 'this is an error'))
.toThrowError(/Expected no matches, but stack has 1 messages as follows:/);
});
});

describe('findError', () => {
test('match', () => {
const result = annotations.findError('*', Match.anyValue());
Expand All @@ -72,6 +83,17 @@ describe('Messages', () => {
});
});

describe('hasNoWarning', () => {
test('match', () => {
annotations.hasNoWarning('/Default/Foo', Match.anyValue());
});

test('no match', () => {
expect(() => annotations.hasNoWarning('/Default/Fred', 'this is a warning'))
.toThrowError(/Expected no matches, but stack has 1 messages as follows:/);
});
});

describe('findWarning', () => {
test('match', () => {
const result = annotations.findWarning('*', Match.anyValue());
Expand All @@ -94,6 +116,17 @@ describe('Messages', () => {
});
});

describe('hasNoInfo', () => {
test('match', () => {
annotations.hasNoInfo('/Default/Qux', 'this info is incorrect');
});

test('no match', () => {
expect(() => annotations.hasNoInfo('/Default/Qux', 'this is an info'))
.toThrowError(/Expected no matches, but stack has 1 messages as follows:/);
});
});

describe('findInfo', () => {
test('match', () => {
const result = annotations.findInfo('/Default/Qux', 'this is an info');
Expand Down

0 comments on commit 6bdc9eb

Please sign in to comment.