Skip to content

Commit d0d6fc5

Browse files
authored
feat(assertions): support for parameters (#18469)
Add the ability to match parameters in the template. Closes #16720 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent f511c17 commit d0d6fc5

File tree

4 files changed

+214
-3
lines changed

4 files changed

+214
-3
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { filterLogicalId, formatFailure, matchSection } from './section';
2+
import { Template } from './template';
3+
4+
export function findParameters(template: Template, logicalId: string, props: any = {}): { [key: string]: { [key: string]: any } } {
5+
const section: { [key: string] : {} } = template.Parameters;
6+
const result = matchSection(filterLogicalId(section, logicalId), props);
7+
8+
if (!result.match) {
9+
return {};
10+
}
11+
12+
return result.matches;
13+
}
14+
15+
export function hasParameter(template: Template, logicalId: string, props: any): string | void {
16+
const section: { [key: string] : {} } = template.Parameters;
17+
const result = matchSection(filterLogicalId(section, logicalId), props);
18+
if (result.match) {
19+
return;
20+
}
21+
22+
if (result.closestResult === undefined) {
23+
return 'No parameters found in the template';
24+
}
25+
26+
return [
27+
`Template has ${result.analyzedCount} parameters, but none match as expected.`,
28+
formatFailure(result.closestResult),
29+
].join('\n');
30+
}

packages/@aws-cdk/assertions/lib/private/template.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
export type Template = {
44
Resources: { [logicalId: string]: Resource },
55
Outputs: { [logicalId: string]: Output },
6-
Mappings: { [logicalId: string]: Mapping }
6+
Mappings: { [logicalId: string]: Mapping },
7+
Parameters: { [logicalId: string]: Parameter }
78
}
89

910
export type Resource = {
@@ -13,4 +14,9 @@ export type Resource = {
1314

1415
export type Output = { [key: string]: any };
1516

16-
export type Mapping = { [key: string]: any };
17+
export type Mapping = { [key: string]: any };
18+
19+
export type Parameter = {
20+
Type: string;
21+
[key: string]: any;
22+
}

packages/@aws-cdk/assertions/lib/template.ts

+25
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Match } from './match';
55
import { Matcher } from './matcher';
66
import { findMappings, hasMapping } from './private/mappings';
77
import { findOutputs, hasOutput } from './private/outputs';
8+
import { findParameters, hasParameter } from './private/parameters';
89
import { countResources, findResources, hasResource, hasResourceProperties } from './private/resources';
910
import { Template as TemplateType } from './private/template';
1011

@@ -108,6 +109,30 @@ export class Template {
108109
return findResources(this.template, type, props);
109110
}
110111

112+
/**
113+
* Assert that a Parameter with the given properties exists in the CloudFormation template.
114+
* By default, performs partial matching on the parameter, via the `Match.objectLike()`.
115+
* To configure different behavior, use other matchers in the `Match` class.
116+
* @param logicalId the name of the parameter. Provide `'*'` to match all parameters in the template.
117+
* @param props the parameter as should be expected in the template.
118+
*/
119+
public hasParameter(logicalId: string, props: any): void {
120+
const matchError = hasParameter(this.template, logicalId, props);
121+
if (matchError) {
122+
throw new Error(matchError);
123+
}
124+
}
125+
126+
/**
127+
* Get the set of matching Parameters that match the given properties in the CloudFormation template.
128+
* @param logicalId the name of the parameter. Provide `'*'` to match all parameters in the template.
129+
* @param props by default, matches all Parameters in the template.
130+
* When a literal object is provided, performs a partial match via `Match.objectLike()`.
131+
* Use the `Match` APIs to configure a different behaviour. */
132+
public findParameters(logicalId: string, props: any = {}): { [key: string]: { [key: string]: any } } {
133+
return findParameters(this.template, logicalId, props);
134+
}
135+
111136
/**
112137
* Assert that an Output with the given properties exists in the CloudFormation template.
113138
* By default, performs partial matching on the resource, via the `Match.objectLike()`.

packages/@aws-cdk/assertions/test/template.test.ts

+151-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { App, CfnMapping, CfnOutput, CfnResource, NestedStack, Stack } from '@aws-cdk/core';
1+
import { App, CfnMapping, CfnOutput, CfnParameter, CfnResource, NestedStack, Stack } from '@aws-cdk/core';
22
import { Construct } from 'constructs';
33
import { Capture, Match, Template } from '../lib';
44

@@ -708,6 +708,156 @@ describe('Template', () => {
708708
});
709709
});
710710

711+
describe('findParameters', () => {
712+
test('matching', () => {
713+
const stack = new Stack();
714+
new CfnParameter(stack, 'p1', {
715+
type: 'String',
716+
description: 'string parameter',
717+
});
718+
new CfnParameter(stack, 'p2', {
719+
type: 'Number',
720+
description: 'number parameter',
721+
});
722+
723+
const inspect = Template.fromStack(stack);
724+
const result = inspect.findParameters('*', { Type: 'String' });
725+
expect(result).toEqual({
726+
p1: {
727+
Description: 'string parameter',
728+
Type: 'String',
729+
},
730+
});
731+
});
732+
733+
test('not matching', () => {
734+
const stack = new Stack();
735+
new CfnParameter(stack, 'p1', {
736+
type: 'String',
737+
description: 'string parameter',
738+
});
739+
740+
const inspect = Template.fromStack(stack);
741+
const result = inspect.findParameters('*', { Type: 'Number' });
742+
expect(Object.keys(result).length).toEqual(0);
743+
});
744+
745+
test('matching with specific parameter name', () => {
746+
const stack = new Stack();
747+
new CfnParameter(stack, 'p1', {
748+
type: 'String',
749+
description: 'string parameter',
750+
});
751+
new CfnParameter(stack, 'p2', {
752+
type: 'Number',
753+
description: 'number parameter',
754+
});
755+
756+
const inspect = Template.fromStack(stack);
757+
const result = inspect.findParameters('p1', { Type: 'String' });
758+
expect(result).toEqual({
759+
p1: {
760+
Description: 'string parameter',
761+
Type: 'String',
762+
},
763+
});
764+
});
765+
766+
test('not matching specific parameter name', () => {
767+
const stack = new Stack();
768+
new CfnParameter(stack, 'p1', {
769+
type: 'String',
770+
description: 'string parameter',
771+
});
772+
new CfnParameter(stack, 'p2', {
773+
type: 'Number',
774+
description: 'number parameter',
775+
});
776+
777+
const inspect = Template.fromStack(stack);
778+
const result = inspect.findParameters('p3', { Type: 'String' });
779+
expect(Object.keys(result).length).toEqual(0);
780+
});
781+
});
782+
783+
describe('hasParameter', () => {
784+
test('matching', () => {
785+
const stack = new Stack();
786+
new CfnParameter(stack, 'p1', {
787+
type: 'String',
788+
description: 'string parameter',
789+
});
790+
new CfnParameter(stack, 'p2', {
791+
type: 'Number',
792+
description: 'number parameter',
793+
});
794+
795+
const inspect = Template.fromStack(stack);
796+
expect(() => inspect.findParameters('p3', { Type: 'String' })).not.toThrow();
797+
});
798+
799+
test('not matching', (done) => {
800+
const stack = new Stack();
801+
new CfnParameter(stack, 'p1', {
802+
type: 'String',
803+
description: 'string parameter',
804+
});
805+
new CfnParameter(stack, 'p2', {
806+
type: 'Number',
807+
description: 'number parameter',
808+
});
809+
810+
const inspect = Template.fromStack(stack);
811+
expectToThrow(
812+
() => inspect.hasParameter('*', { Type: 'CommaDelimitedList' }),
813+
[
814+
/2 parameters/,
815+
/Expected CommaDelimitedList but received String/,
816+
],
817+
done,
818+
);
819+
done();
820+
});
821+
822+
test('matching specific parameter name', () => {
823+
const stack = new Stack();
824+
new CfnParameter(stack, 'p1', {
825+
type: 'String',
826+
description: 'string parameter',
827+
});
828+
new CfnParameter(stack, 'p2', {
829+
type: 'Number',
830+
description: 'number parameter',
831+
});
832+
833+
const inspect = Template.fromStack(stack);
834+
expect(() => inspect.findParameters('p1', { Type: 'String' })).not.toThrow();
835+
});
836+
837+
test('not matching specific parameter name', (done) => {
838+
const stack = new Stack();
839+
new CfnParameter(stack, 'p1', {
840+
type: 'String',
841+
description: 'string parameter',
842+
});
843+
new CfnParameter(stack, 'p2', {
844+
type: 'Number',
845+
description: 'number parameter',
846+
});
847+
848+
const inspect = Template.fromStack(stack);
849+
expectToThrow(
850+
() => inspect.hasParameter('p2', { Type: 'CommaDelimitedList' }),
851+
[
852+
/1 parameter/,
853+
/Expected CommaDelimitedList but received Number/,
854+
],
855+
done,
856+
);
857+
done();
858+
});
859+
});
860+
711861
describe('findMappings', () => {
712862
test('matching', () => {
713863
const stack = new Stack();

0 commit comments

Comments
 (0)