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(assertions): support for parameters #18469

Merged
merged 4 commits into from
Jan 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions packages/@aws-cdk/assertions/lib/private/parameters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { filterLogicalId, formatFailure, matchSection } from './section';
import { Template } from './template';

export function findParameters(template: Template, logicalId: string, props: any = {}): { [key: string]: { [key: string]: any } } {
const section: { [key: string] : {} } = template.Parameters;
const result = matchSection(filterLogicalId(section, logicalId), props);

if (!result.match) {
return {};
}

return result.matches;
}

export function hasParameter(template: Template, logicalId: string, props: any): string | void {
const section: { [key: string] : {} } = template.Parameters;
const result = matchSection(filterLogicalId(section, logicalId), props);
if (result.match) {
return;
}

if (result.closestResult === undefined) {
return 'No parameters found in the template';
}

return [
`Template has ${result.analyzedCount} parameters, but none match as expected.`,
formatFailure(result.closestResult),
].join('\n');
}
10 changes: 8 additions & 2 deletions packages/@aws-cdk/assertions/lib/private/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
export type Template = {
Resources: { [logicalId: string]: Resource },
Outputs: { [logicalId: string]: Output },
Mappings: { [logicalId: string]: Mapping }
Mappings: { [logicalId: string]: Mapping },
Parameters: { [logicalId: string]: Parameter }
}

export type Resource = {
Expand All @@ -13,4 +14,9 @@ export type Resource = {

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

export type Mapping = { [key: string]: any };
export type Mapping = { [key: string]: any };

export type Parameter = {
Type: string;
[key: string]: any;
}
25 changes: 25 additions & 0 deletions packages/@aws-cdk/assertions/lib/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Match } from './match';
import { Matcher } from './matcher';
import { findMappings, hasMapping } from './private/mappings';
import { findOutputs, hasOutput } from './private/outputs';
import { findParameters, hasParameter } from './private/parameters';
import { countResources, findResources, hasResource, hasResourceProperties } from './private/resources';
import { Template as TemplateType } from './private/template';

Expand Down Expand Up @@ -108,6 +109,30 @@ export class Template {
return findResources(this.template, type, props);
}

/**
* Assert that a Parameter with the given properties exists in the CloudFormation template.
* By default, performs partial matching on the parameter, via the `Match.objectLike()`.
* To configure different behavior, use other matchers in the `Match` class.
* @param logicalId the name of the parameter. Provide `'*'` to match all parameters in the template.
* @param props the parameter as should be expected in the template.
*/
public hasParameter(logicalId: string, props: any): void {
const matchError = hasParameter(this.template, logicalId, props);
if (matchError) {
throw new Error(matchError);
}
}

/**
* Get the set of matching Parameters that match the given properties in the CloudFormation template.
* @param logicalId the name of the parameter. Provide `'*'` to match all parameters in the template.
* @param props by default, matches all Parameters in the template.
* When a literal object is provided, performs a partial match via `Match.objectLike()`.
* Use the `Match` APIs to configure a different behaviour. */
public findParameters(logicalId: string, props: any = {}): { [key: string]: { [key: string]: any } } {
return findParameters(this.template, logicalId, props);
}

/**
* Assert that an Output with the given properties exists in the CloudFormation template.
* By default, performs partial matching on the resource, via the `Match.objectLike()`.
Expand Down
152 changes: 151 additions & 1 deletion packages/@aws-cdk/assertions/test/template.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { App, CfnMapping, CfnOutput, CfnResource, NestedStack, Stack } from '@aws-cdk/core';
import { App, CfnMapping, CfnOutput, CfnParameter, CfnResource, NestedStack, Stack } from '@aws-cdk/core';
import { Construct } from 'constructs';
import { Capture, Match, Template } from '../lib';

Expand Down Expand Up @@ -708,6 +708,156 @@ describe('Template', () => {
});
});

describe('findParameters', () => {
test('matching', () => {
const stack = new Stack();
new CfnParameter(stack, 'p1', {
type: 'String',
description: 'string parameter',
});
new CfnParameter(stack, 'p2', {
type: 'Number',
description: 'number parameter',
});

const inspect = Template.fromStack(stack);
const result = inspect.findParameters('*', { Type: 'String' });
expect(result).toEqual({
p1: {
Description: 'string parameter',
Type: 'String',
},
});
});

test('not matching', () => {
const stack = new Stack();
new CfnParameter(stack, 'p1', {
type: 'String',
description: 'string parameter',
});

const inspect = Template.fromStack(stack);
const result = inspect.findParameters('*', { Type: 'Number' });
expect(Object.keys(result).length).toEqual(0);
});

test('matching with specific parameter name', () => {
const stack = new Stack();
new CfnParameter(stack, 'p1', {
type: 'String',
description: 'string parameter',
});
new CfnParameter(stack, 'p2', {
type: 'Number',
description: 'number parameter',
});

const inspect = Template.fromStack(stack);
const result = inspect.findParameters('p1', { Type: 'String' });
expect(result).toEqual({
p1: {
Description: 'string parameter',
Type: 'String',
},
});
});

test('not matching specific parameter name', () => {
const stack = new Stack();
new CfnParameter(stack, 'p1', {
type: 'String',
description: 'string parameter',
});
new CfnParameter(stack, 'p2', {
type: 'Number',
description: 'number parameter',
});

const inspect = Template.fromStack(stack);
const result = inspect.findParameters('p3', { Type: 'String' });
expect(Object.keys(result).length).toEqual(0);
});
});

describe('hasParameter', () => {
test('matching', () => {
const stack = new Stack();
new CfnParameter(stack, 'p1', {
type: 'String',
description: 'string parameter',
});
new CfnParameter(stack, 'p2', {
type: 'Number',
description: 'number parameter',
});

const inspect = Template.fromStack(stack);
expect(() => inspect.findParameters('p3', { Type: 'String' })).not.toThrow();
});

test('not matching', (done) => {
const stack = new Stack();
new CfnParameter(stack, 'p1', {
type: 'String',
description: 'string parameter',
});
new CfnParameter(stack, 'p2', {
type: 'Number',
description: 'number parameter',
});

const inspect = Template.fromStack(stack);
expectToThrow(
() => inspect.hasParameter('*', { Type: 'CommaDelimitedList' }),
[
/2 parameters/,
/Expected CommaDelimitedList but received String/,
],
done,
);
done();
});

test('matching specific parameter name', () => {
const stack = new Stack();
new CfnParameter(stack, 'p1', {
type: 'String',
description: 'string parameter',
});
new CfnParameter(stack, 'p2', {
type: 'Number',
description: 'number parameter',
});

const inspect = Template.fromStack(stack);
expect(() => inspect.findParameters('p1', { Type: 'String' })).not.toThrow();
});

test('not matching specific parameter name', (done) => {
const stack = new Stack();
new CfnParameter(stack, 'p1', {
type: 'String',
description: 'string parameter',
});
new CfnParameter(stack, 'p2', {
type: 'Number',
description: 'number parameter',
});

const inspect = Template.fromStack(stack);
expectToThrow(
() => inspect.hasParameter('p2', { Type: 'CommaDelimitedList' }),
[
/1 parameter/,
/Expected CommaDelimitedList but received Number/,
],
done,
);
done();
});
});

describe('findMappings', () => {
test('matching', () => {
const stack = new Stack();
Expand Down