From f1e7ef4e4f34613e92d33538f2c7c3d056be8cb5 Mon Sep 17 00:00:00 2001 From: Kelly Towle Date: Fri, 7 Feb 2020 09:15:48 -0800 Subject: [PATCH 1/3] feat(assert): add countResourcesLike method Adds a method to get a count of Stack resources filtered by Type (i.e. `"AWS::ApiGateway::Method"`) as well as properties (i.e. `{ resourceId: "MyResource01234" }`) --- .../assert/lib/assertions/count-resources.ts | 28 ++++++-- .../@aws-cdk/assert/test/assertions.test.ts | 71 ++++++++++++++++++- 2 files changed, 94 insertions(+), 5 deletions(-) diff --git a/packages/@aws-cdk/assert/lib/assertions/count-resources.ts b/packages/@aws-cdk/assert/lib/assertions/count-resources.ts index 481a72cb0c0dd..52e96a188e220 100644 --- a/packages/@aws-cdk/assert/lib/assertions/count-resources.ts +++ b/packages/@aws-cdk/assert/lib/assertions/count-resources.ts @@ -8,12 +8,22 @@ export function countResources(resourceType: string, count = 1): Assertion { + return new CountResourcesAssertion(resourceType, count, props); +} + class CountResourcesAssertion extends Assertion { private inspected: number = 0; + private readonly props: any; constructor(private readonly resourceType: string, - private readonly count: number) { + private readonly count: number, + props: any = null) { super(); + this.props = props; } public assertUsing(inspector: StackInspector): boolean { @@ -21,8 +31,18 @@ class CountResourcesAssertion extends Assertion { for (const logicalId of Object.keys(inspector.value.Resources || {})) { const resource = inspector.value.Resources[logicalId]; if (resource.Type === this.resourceType) { - counted++; - this.inspected += 1; + if (this.props) { + const propEntries = Object.entries(this.props); + propEntries.forEach(([key, val]) => { + if (resource.Properties && resource.Properties[key] && JSON.stringify(resource.Properties[key]) === JSON.stringify(val)) { + counted++; + this.inspected += 1; + } + }); + } else { + counted++; + this.inspected += 1; + } } } @@ -30,6 +50,6 @@ class CountResourcesAssertion extends Assertion { } public get description(): string { - return `stack only has ${this.inspected} resource of type ${this.resourceType} but we expected to find ${this.count}`; + return `stack only has ${this.inspected} resource of type ${this.resourceType}${this.props ? ' with specified properties' : ''} but we expected to find ${this.count}`; } } diff --git a/packages/@aws-cdk/assert/test/assertions.test.ts b/packages/@aws-cdk/assert/test/assertions.test.ts index 26e5cb1707c5f..d5a7f56132e36 100644 --- a/packages/@aws-cdk/assert/test/assertions.test.ts +++ b/packages/@aws-cdk/assert/test/assertions.test.ts @@ -1,7 +1,7 @@ import * as cdk from '@aws-cdk/core'; import * as cx from '@aws-cdk/cx-api'; -import { countResources, exist, expect as cdkExpect, haveType, MatchStyle, matchTemplate } from '../lib/index'; +import { countResources, countResourcesLike, exist, expect as cdkExpect, haveType, MatchStyle, matchTemplate } from '../lib/index'; passingExample('expect at to have ', () => { const resourceType = 'Test::Resource'; @@ -237,6 +237,75 @@ failingExample('expect to count resources - less than expected', () cdkExpect(synthStack).to(countResources(resourceType, 0)); }); +// countResourcesLike + +passingExample('expect to count resources like props - as expected', () => { + const synthStack = synthesizedStack(stack => { + new TestResource(stack, 'R1', { type: 'Bar', properties: { parentId: 123, name: "A" } }); + new TestResource(stack, 'R2', { type: 'Bar', properties: { parentId: 123, name: "B" } }); + new TestResource(stack, 'R3', { type: 'Foo', properties: { parentId: 123 } }); + }); + + cdkExpect(synthStack).to(countResourcesLike('Bar', 2, { parentId: 123 })); + cdkExpect(synthStack).to(countResourcesLike('Foo', 1, { parentId: 123 })); +}); + +passingExample('expect to count resources like props - expected no resources', () => { + const resourceType = 'Test::Resource'; + const stack = new cdk.Stack(); + cdkExpect(stack).to(countResourcesLike(resourceType, 0, { parentId: 123 })); +}); + +passingExample('expect to count resources like props - expected no resources', () => { + const resourceType = 'Test::Resource'; + const synthStack = synthesizedStack(stack => { + new TestResource(stack, 'R1', { type: resourceType, properties: { parentId: 123, name: "A" } }); + new TestResource(stack, 'R2', { type: resourceType }); + new TestResource(stack, 'R3', { type: 'Foo', properties: { parentId: 456} }); + }); + cdkExpect(synthStack).to(countResourcesLike(resourceType, 0, { parentId: 456 })); +}); + +failingExample('expect to count resources like props - more than expected', () => { + const resourceType = 'Test::Resource'; + const synthStack = synthesizedStack(stack => { + new TestResource(stack, 'R1', { type: resourceType, properties: { parentId: 123 } }); + new TestResource(stack, 'R2', { type: resourceType, properties: { parentId: 123 } }); + }); + + cdkExpect(synthStack).to(countResourcesLike(resourceType, 1, { parentId: 123 })); +}); + +failingExample('expect to count resources like props - nested props out of order', () => { + const resourceType = 'Test::Resource'; + const synthStack = synthesizedStack(stack => { + new TestResource(stack, 'R1', { type: resourceType, properties: { id: 987, parentInfo: { id: 123, name: "A" } } }); + new TestResource(stack, 'R2', { type: resourceType, properties: { id: 456, parentInfo: { name: "A", id: 123 } } }); + }); + + cdkExpect(synthStack).to(countResourcesLike(resourceType, 2, { parentInfo: { id: 123, name: "A" } })); +}); + +failingExample('expect to count resources like props - nested props incomplete', () => { + const resourceType = 'Test::Resource'; + const synthStack = synthesizedStack(stack => { + new TestResource(stack, 'R1', { type: resourceType, properties: { id: 987, parentInfo: { id: 123, name: "A" } } }); + new TestResource(stack, 'R2', { type: resourceType, properties: { id: 456, parentInfo: { name: "A", id: 123 } } }); + }); + + cdkExpect(synthStack).to(countResourcesLike(resourceType, 2, { parentInfo: { id: 123 } })); +}); + +failingExample('expect to count resources like props - less than expected', () => { + const resourceType = 'Test::Resource'; + const synthStack = synthesizedStack(stack => { + new TestResource(stack, 'R1', { type: resourceType, properties: { parentId: 123 } }); + new TestResource(stack, 'R2', { type: resourceType, properties: { parentId: 123 } }); + }); + + cdkExpect(synthStack).to(countResourcesLike(resourceType, 0, { parentId: 123 })); +}); + function passingExample(title: string, cb: () => void) { describe('passing', () => { test(title, cb); From be2552cfd1db368f42f9d6582e69bd5077020779 Mon Sep 17 00:00:00 2001 From: Kelly Towle Date: Sun, 9 Feb 2020 14:22:27 -0800 Subject: [PATCH 2/3] feat(assert): add countResourcesLike method Adds documentation for `countResources` and `countResourcesLike` methods --- packages/@aws-cdk/assert/README.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/packages/@aws-cdk/assert/README.md b/packages/@aws-cdk/assert/README.md index 25a4fa7df367b..11aae46288eb4 100644 --- a/packages/@aws-cdk/assert/README.md +++ b/packages/@aws-cdk/assert/README.md @@ -84,6 +84,26 @@ expect(stack).to(haveResource('AWS::CertificateManager::Certificate', { `ABSENT` is a magic value to assert that a particular key in an object is *not* set (or set to `undefined`). +### Check number of resources + +If you want to assert that `n` number of resources of a particular type exist, with or without specific properties: + +```ts +countResource(type, count) +countResourceLike(type, count, props) +``` + +Example: + +```ts +expect(stack).to(countResources('AWS::ApiGateway::Method', 3)); +expect(stack).to(countResourcesLike('AWS::ApiGateway::Method', 1, { + HttpMethod: 'GET', + ResourceId: { + "Ref": "MyResource01234" + } +})); +``` ### Check existence of an output `haveOutput` assertion can be used to check that a stack contains specific output. From f8f70644685867d3d423e0ac1df543f7c5beb415 Mon Sep 17 00:00:00 2001 From: Elad Ben-Israel Date: Mon, 10 Feb 2020 09:00:46 +0200 Subject: [PATCH 3/3] Update README.md --- packages/@aws-cdk/assert/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/assert/README.md b/packages/@aws-cdk/assert/README.md index 11aae46288eb4..2657ebdf92cc1 100644 --- a/packages/@aws-cdk/assert/README.md +++ b/packages/@aws-cdk/assert/README.md @@ -89,8 +89,8 @@ expect(stack).to(haveResource('AWS::CertificateManager::Certificate', { If you want to assert that `n` number of resources of a particular type exist, with or without specific properties: ```ts -countResource(type, count) -countResourceLike(type, count, props) +countResources(type, count) +countResourcesLike(type, count, props) ``` Example: