Skip to content

Commit 532a72b

Browse files
authored
feat(assertions): findXxx() APIs now includes the logical id as part of its result (#16454)
The current return type of `findResources()`, `findOutputs()`, and `findMappings()` omits the `logicalId`. This creates ambiguity because it is not trivial to figure out which resource/output/mapping is being matched. For example, ```ts const result = assert.findOutputs('*', { Value: 'Fred', }); ``` Could return a list like: ```ts expect(result).toEqual([ { Value: 'Fred', Description: 'FooFred' }, { Value: 'Fred', Description: 'BarFred' }, ]); ``` This does not provide information on which output(s) are being matched to the `Value: 'Fred'` property. Under the new return type proposed in this PR, the output would look like this: ```ts expect(result).toEqual({ Foo: { Value: 'Fred', Description: 'FooFred' }, Bar: { Value: 'Fred', Description: 'BarFred' }, }); ``` Returning the `logicalId` in this way for all `find*()` APIs will provide the full picture back to the user. BREAKING CHANGE: the `findResources()` API previously returned a list of resources, but now returns a map of logical id to resource. * **assertions:** the `findOutputs()` API previously returned a list of outputs, but now returns a map of logical id to output. * **assertions:** the `findMappings()` API previously returned a list of mappings, but now returns a map of logical id to mapping. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent ae4aa95 commit 532a72b

File tree

10 files changed

+70
-53
lines changed

10 files changed

+70
-53
lines changed

packages/@aws-cdk/assertions/README.md

+9-1
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,17 @@ assert.hasOutput('*', {
129129
});
130130
```
131131

132-
`findOutputs()` will return a list of outputs that match the `logicalId` and `props`,
132+
`findOutputs()` will return a set of outputs that match the `logicalId` and `props`,
133133
and you can use the `'*'` special case as well.
134134

135+
```ts
136+
const result = assert.findOutputs('*', {
137+
Value: 'Fred',
138+
});
139+
expect(result.Foo).toEqual({ Value: 'Fred', Description: 'FooFred' });
140+
expect(result.Bar).toEqual({ Value: 'Fred', Description: 'BarFred' });
141+
```
142+
135143
The APIs `hasMapping()` and `findMappings()` provide similar functionalities.
136144

137145
## Special Matchers

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import { StackInspector } from '../vendored/assert';
22
import { filterLogicalId, formatFailure, matchSection } from './section';
33

4-
export function findMappings(inspector: StackInspector, logicalId: string, props: any = {}): { [key: string]: any }[] {
4+
export function findMappings(inspector: StackInspector, logicalId: string, props: any = {}): { [key: string]: { [key: string]: any } } {
55
const section: { [key: string] : {} } = inspector.value.Mappings;
66
const result = matchSection(filterLogicalId(section, logicalId), props);
77

88
if (!result.match) {
9-
return [];
9+
return {};
1010
}
1111

1212
return result.matches;

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import { StackInspector } from '../vendored/assert';
22
import { filterLogicalId, formatFailure, matchSection } from './section';
33

4-
export function findOutputs(inspector: StackInspector, logicalId: string, props: any = {}): { [key: string]: any }[] {
4+
export function findOutputs(inspector: StackInspector, logicalId: string, props: any = {}): { [key: string]: { [key: string]: any } } {
55
const section: { [key: string] : {} } = inspector.value.Outputs;
66
const result = matchSection(filterLogicalId(section, logicalId), props);
77

88
if (!result.match) {
9-
return [];
9+
return {};
1010
}
1111

1212
return result.matches;

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ type Resource = {
66
Type: string;
77
}
88

9-
export function findResources(inspector: StackInspector, type: string, props: any = {}): { [key: string]: any }[] {
9+
export function findResources(inspector: StackInspector, type: string, props: any = {}): { [key: string]: { [key: string]: any } } {
1010
const section: { [key: string] : Resource } = inspector.value.Resources;
1111
const result = matchSection(filterType(section, type), props);
1212

1313
if (!result.match) {
14-
return [];
14+
return {};
1515
}
1616

1717
return result.matches;

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

+7-8
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
import { Match } from '../match';
22
import { Matcher, MatchResult } from '../matcher';
33

4-
export type MatchSuccess = { match: true, matches: any[] };
4+
export type MatchSuccess = { match: true, matches: {[key: string]: any} };
55
export type MatchFailure = { match: false, closestResult?: MatchResult, analyzedCount: number };
66

77
export function matchSection(section: any, props: any): MatchSuccess | MatchFailure {
88
const matcher = Matcher.isMatcher(props) ? props : Match.objectLike(props);
99
let closestResult: MatchResult | undefined = undefined;
10-
let matching: any[] = [];
10+
let matching: {[key: string]: any} = {};
1111
let count = 0;
1212

1313
eachEntryInSection(
1414
section,
1515

16-
(entry) => {
16+
(logicalId, entry) => {
1717
const result = matcher.test(entry);
1818
if (!result.hasFailed()) {
19-
matching.push(entry);
19+
matching[logicalId] = entry;
2020
} else {
2121
count++;
2222
if (closestResult === undefined || closestResult.failCount > result.failCount) {
@@ -25,8 +25,7 @@ export function matchSection(section: any, props: any): MatchSuccess | MatchFail
2525
}
2626
},
2727
);
28-
29-
if (matching.length > 0) {
28+
if (Object.keys(matching).length > 0) {
3029
return { match: true, matches: matching };
3130
} else {
3231
return { match: false, closestResult, analyzedCount: count };
@@ -35,11 +34,11 @@ export function matchSection(section: any, props: any): MatchSuccess | MatchFail
3534

3635
function eachEntryInSection(
3736
section: any,
38-
cb: (entry: {[key: string]: any}) => void): void {
37+
cb: (logicalId: string, entry: {[key: string]: any}) => void): void {
3938

4039
for (const logicalId of Object.keys(section ?? {})) {
4140
const resource: { [key: string]: any } = section[logicalId];
42-
cb(resource);
41+
cb(logicalId, resource);
4342
}
4443
}
4544

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ export class Template {
101101
* When a literal is provided, performs a partial match via `Match.objectLike()`.
102102
* Use the `Match` APIs to configure a different behaviour.
103103
*/
104-
public findResources(type: string, props: any = {}): { [key: string]: any }[] {
104+
public findResources(type: string, props: any = {}): { [key: string]: { [key: string]: any } } {
105105
return findResources(this.inspector, type, props);
106106
}
107107

@@ -126,7 +126,7 @@ export class Template {
126126
* When a literal object is provided, performs a partial match via `Match.objectLike()`.
127127
* Use the `Match` APIs to configure a different behaviour.
128128
*/
129-
public findOutputs(logicalId: string, props: any = {}): { [key: string]: any }[] {
129+
public findOutputs(logicalId: string, props: any = {}): { [key: string]: { [key: string]: any } } {
130130
return findOutputs(this.inspector, logicalId, props);
131131
}
132132

@@ -151,7 +151,7 @@ export class Template {
151151
* When a literal object is provided, performs a partial match via `Match.objectLike()`.
152152
* Use the `Match` APIs to configure a different behaviour.
153153
*/
154-
public findMappings(logicalId: string, props: any = {}): { [key: string]: any }[] {
154+
public findMappings(logicalId: string, props: any = {}): { [key: string]: { [key: string]: any } } {
155155
return findMappings(this.inspector, logicalId, props);
156156
}
157157

packages/@aws-cdk/assertions/test/private/section.test.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ describe('section', () => {
1818
// THEN
1919
expect(result.match).toEqual(true);
2020
const success = result as MatchSuccess;
21-
expect(success.matches.length).toEqual(2);
21+
expect(Object.keys(success.matches).length).toEqual(2);
22+
expect(success.matches.Entry1).toEqual({ foo: 'bar' });
23+
expect(success.matches.Entry2).toEqual({ foo: 'bar', baz: 'qux' });
2224
});
2325

2426
test('failure', () => {

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

+34-28
Original file line numberDiff line numberDiff line change
@@ -278,10 +278,12 @@ describe('Template', () => {
278278
});
279279

280280
const inspect = Template.fromStack(stack);
281-
expect(inspect.findResources('Foo::Bar')).toEqual([{
282-
Type: 'Foo::Bar',
283-
Properties: { baz: 'qux', fred: 'waldo' },
284-
}]);
281+
expect(inspect.findResources('Foo::Bar')).toEqual({
282+
Foo: {
283+
Type: 'Foo::Bar',
284+
Properties: { baz: 'qux', fred: 'waldo' },
285+
},
286+
});
285287
});
286288

287289
test('no matching resource type', () => {
@@ -292,7 +294,7 @@ describe('Template', () => {
292294
});
293295

294296
const inspect = Template.fromStack(stack);
295-
expect(inspect.findResources('Foo::Baz')).toEqual([]);
297+
expect(inspect.findResources('Foo::Baz')).toEqual({});
296298
});
297299

298300
test('matching resource props', () => {
@@ -303,9 +305,9 @@ describe('Template', () => {
303305
});
304306

305307
const inspect = Template.fromStack(stack);
306-
expect(inspect.findResources('Foo::Bar', {
308+
expect(Object.keys(inspect.findResources('Foo::Bar', {
307309
Properties: { baz: 'qux' },
308-
}).length).toEqual(1);
310+
})).length).toEqual(1);
309311
});
310312

311313
test('no matching resource props', () => {
@@ -318,7 +320,7 @@ describe('Template', () => {
318320
const inspect = Template.fromStack(stack);
319321
expect(inspect.findResources('Foo::Bar', {
320322
Properties: { baz: 'waldo' },
321-
})).toEqual([]);
323+
})).toEqual({});
322324
});
323325

324326
test('multiple matching resources', () => {
@@ -327,7 +329,10 @@ describe('Template', () => {
327329
new CfnResource(stack, 'Bar', { type: 'Foo::Bar' });
328330

329331
const inspect = Template.fromStack(stack);
330-
expect(inspect.findResources('Foo::Bar').length).toEqual(2);
332+
const result = inspect.findResources('Foo::Bar');
333+
expect(Object.keys(result).length).toEqual(2);
334+
expect(result.Foo).toEqual({ Type: 'Foo::Bar' });
335+
expect(result.Bar).toEqual({ Type: 'Foo::Bar' });
331336
});
332337
});
333338

@@ -433,10 +438,9 @@ describe('Template', () => {
433438

434439
const inspect = Template.fromStack(stack);
435440
const result = inspect.findOutputs('*', { Value: 'Fred' });
436-
expect(result).toEqual([
437-
{ Value: 'Fred', Description: 'FooFred' },
438-
{ Value: 'Fred', Description: 'BarFred' },
439-
]);
441+
expect(Object.keys(result).length).toEqual(2);
442+
expect(result.Foo).toEqual({ Value: 'Fred', Description: 'FooFred' });
443+
expect(result.Bar).toEqual({ Value: 'Fred', Description: 'BarFred' });
440444
});
441445

442446
test('not matching', () => {
@@ -447,7 +451,7 @@ describe('Template', () => {
447451

448452
const inspect = Template.fromStack(stack);
449453
const result = inspect.findOutputs('*', { Value: 'Waldo' });
450-
expect(result.length).toEqual(0);
454+
expect(Object.keys(result).length).toEqual(0);
451455
});
452456

453457
test('matching specific output', () => {
@@ -461,9 +465,11 @@ describe('Template', () => {
461465

462466
const inspect = Template.fromStack(stack);
463467
const result = inspect.findOutputs('Foo', { Value: 'Fred' });
464-
expect(result).toEqual([
465-
{ Value: 'Fred' },
466-
]);
468+
expect(result).toEqual({
469+
Foo: {
470+
Value: 'Fred',
471+
},
472+
});
467473
});
468474

469475
test('not matching specific output', () => {
@@ -477,7 +483,7 @@ describe('Template', () => {
477483

478484
const inspect = Template.fromStack(stack);
479485
const result = inspect.findOutputs('Foo', { Value: 'Waldo' });
480-
expect(result.length).toEqual(0);
486+
expect(Object.keys(result).length).toEqual(0);
481487
});
482488
});
483489

@@ -592,13 +598,13 @@ describe('Template', () => {
592598

593599
const inspect = Template.fromStack(stack);
594600
const result = inspect.findMappings('*', { Foo: { Bar: 'Lightning' } });
595-
expect(result).toEqual([
596-
{
601+
expect(result).toEqual({
602+
Foo: {
597603
Foo: { Bar: 'Lightning', Fred: 'Waldo' },
598604
Baz: { Bar: 'Qux' },
599605
},
600-
{ Foo: { Bar: 'Lightning' } },
601-
]);
606+
Fred: { Foo: { Bar: 'Lightning' } },
607+
});
602608
});
603609

604610
test('not matching', () => {
@@ -611,7 +617,7 @@ describe('Template', () => {
611617

612618
const inspect = Template.fromStack(stack);
613619
const result = inspect.findMappings('*', { Foo: { Bar: 'Waldo' } });
614-
expect(result.length).toEqual(0);
620+
expect(Object.keys(result).length).toEqual(0);
615621
});
616622

617623
test('matching with specific outputName', () => {
@@ -630,15 +636,15 @@ describe('Template', () => {
630636

631637
const inspect = Template.fromStack(stack);
632638
const result = inspect.findMappings('Foo', { Foo: { Bar: 'Lightning' } });
633-
expect(result).toEqual([
634-
{
639+
expect(result).toEqual({
640+
Foo: {
635641
Foo: { Bar: 'Lightning', Fred: 'Waldo' },
636642
Baz: { Bar: 'Qux' },
637643
},
638-
]);
644+
});
639645
});
640646

641-
test('not matching', () => {
647+
test('not matching specific output name', () => {
642648
const stack = new Stack();
643649
new CfnMapping(stack, 'Foo', {
644650
mapping: {
@@ -654,7 +660,7 @@ describe('Template', () => {
654660

655661
const inspect = Template.fromStack(stack);
656662
const result = inspect.findMappings('Fred', { Baz: { Bar: 'Qux' } });
657-
expect(result.length).toEqual(0);
663+
expect(Object.keys(result).length).toEqual(0);
658664
});
659665
});
660666
});

packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -267,9 +267,9 @@ describe('HttpApi', () => {
267267
Template.fromStack(stack).hasResourceProperties('AWS::ApiGatewayV2::VpcLink', {
268268
Name: 'Link-1',
269269
});
270-
expect(Template.fromStack(stack).findResources('AWS::ApiGatewayV2::VpcLink', {
270+
expect(Object.keys(Template.fromStack(stack).findResources('AWS::ApiGatewayV2::VpcLink', {
271271
Name: 'Link-2',
272-
}).length).toEqual(0);
272+
})).length).toEqual(0);
273273
});
274274

275275
test('apiEndpoint is exported', () => {

packages/@aws-cdk/aws-cloudwatch/test/dashboard.test.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ describe('Dashboard', () => {
2727

2828
// THEN
2929
const resources = Template.fromStack(stack).findResources('AWS::CloudWatch::Dashboard');
30-
expect(resources.length).toEqual(1);
31-
hasWidgets(resources[0].Properties, [
30+
expect(Object.keys(resources).length).toEqual(1);
31+
const key = Object.keys(resources)[0];
32+
hasWidgets(resources[key].Properties, [
3233
{ type: 'text', width: 10, height: 2, x: 0, y: 0, properties: { markdown: 'first' } },
3334
{ type: 'text', width: 1, height: 4, x: 0, y: 2, properties: { markdown: 'second' } },
3435
{ type: 'text', width: 4, height: 1, x: 0, y: 6, properties: { markdown: 'third' } },
@@ -63,8 +64,9 @@ describe('Dashboard', () => {
6364

6465
// THEN
6566
const resources = Template.fromStack(stack).findResources('AWS::CloudWatch::Dashboard');
66-
expect(resources.length).toEqual(1);
67-
hasWidgets(resources[0].Properties, [
67+
expect(Object.keys(resources).length).toEqual(1);
68+
const key = Object.keys(resources)[0];
69+
hasWidgets(resources[key].Properties, [
6870
{ type: 'text', width: 10, height: 2, x: 0, y: 0, properties: { markdown: 'first' } },
6971
{ type: 'text', width: 1, height: 4, x: 10, y: 0, properties: { markdown: 'second' } },
7072
{ type: 'text', width: 4, height: 1, x: 11, y: 0, properties: { markdown: 'third' } },

0 commit comments

Comments
 (0)