From 8c1be1ee2c0ea2ba45a67ef6fbbe41fa426d9328 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy <36202692+kaizencc@users.noreply.github.com> Date: Fri, 17 Jan 2025 18:26:29 -0500 Subject: [PATCH] test(toolkit): more deploy tests (#33000) These are tests ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/toolkit/lib/toolkit/toolkit.ts | 2 +- .../stack-with-notification-arns/index.ts | 12 ++ .../toolkit/test/actions/deploy.test.ts | 162 ++++++++++++++++-- 3 files changed, 163 insertions(+), 13 deletions(-) create mode 100644 packages/@aws-cdk/toolkit/test/_fixtures/stack-with-notification-arns/index.ts diff --git a/packages/@aws-cdk/toolkit/lib/toolkit/toolkit.ts b/packages/@aws-cdk/toolkit/lib/toolkit/toolkit.ts index cdc8d63f1a34b..061298d027974 100644 --- a/packages/@aws-cdk/toolkit/lib/toolkit/toolkit.ts +++ b/packages/@aws-cdk/toolkit/lib/toolkit/toolkit.ts @@ -394,7 +394,7 @@ export class Toolkit extends CloudAssemblySourceBuilder implements AsyncDisposab if (!confirmed) { throw new ToolkitError('Aborted by user'); } } - // Go around through the 'while' loop again but switch rollback to false. + // Go around through the 'while' loop again but switch rollback to true. rollback = true; break; } diff --git a/packages/@aws-cdk/toolkit/test/_fixtures/stack-with-notification-arns/index.ts b/packages/@aws-cdk/toolkit/test/_fixtures/stack-with-notification-arns/index.ts new file mode 100644 index 0000000000000..055db902b49e5 --- /dev/null +++ b/packages/@aws-cdk/toolkit/test/_fixtures/stack-with-notification-arns/index.ts @@ -0,0 +1,12 @@ +import * as core from 'aws-cdk-lib/core'; + +export default async() => { + const app = new core.App(); + new core.Stack(app, 'Stack1', { + notificationArns: [ + 'arn:aws:sns:us-east-1:1111111111:resource', + 'arn:aws:sns:us-east-1:1111111111:other-resource', + ], + }); + return app.synth() as any; +}; diff --git a/packages/@aws-cdk/toolkit/test/actions/deploy.test.ts b/packages/@aws-cdk/toolkit/test/actions/deploy.test.ts index 8604cf5bc4648..b29a20f9c729e 100644 --- a/packages/@aws-cdk/toolkit/test/actions/deploy.test.ts +++ b/packages/@aws-cdk/toolkit/test/actions/deploy.test.ts @@ -1,20 +1,23 @@ -import { RequireApproval } from '../../lib'; +import { RequireApproval, StackParameters } from '../../lib'; import { Toolkit } from '../../lib/toolkit'; import { builderFixture, TestIoHost } from '../_helpers'; const ioHost = new TestIoHost(); const toolkit = new Toolkit({ ioHost }); +jest.spyOn(toolkit, 'rollback').mockResolvedValue(); + +let mockDeployStack = jest.fn().mockResolvedValue({ + type: 'did-deploy-stack', + stackArn: 'arn:aws:cloudformation:region:account:stack/test-stack', + outputs: {}, + noOp: false, +}); jest.mock('../../lib/api/aws-cdk', () => { return { ...jest.requireActual('../../lib/api/aws-cdk'), Deployments: jest.fn().mockImplementation(() => ({ - deployStack: jest.fn().mockResolvedValue({ - type: 'did-deploy-stack', - stackArn: 'arn:aws:cloudformation:region:account:stack/test-stack', - outputs: {}, - noOp: false, - }), + deployStack: mockDeployStack, resolveEnvironment: jest.fn().mockResolvedValue({}), isSingleAssetPublished: jest.fn().mockResolvedValue(true), readCurrentTemplate: jest.fn().mockResolvedValue({ Resources: {} }), @@ -35,11 +38,7 @@ describe('deploy', () => { await toolkit.deploy(cx); // THEN - expect(ioHost.notifySpy).toHaveBeenCalledWith(expect.objectContaining({ - action: 'deploy', - level: 'info', - message: expect.stringContaining('Deployment time:'), - })); + successfulDeployment(); }); test('request response when require approval is set', async () => { @@ -73,4 +72,143 @@ describe('deploy', () => { message: expect.stringContaining('Do you wish to deploy these changes'), })); }); + + describe('deployment options', () => { + test('parameters are passed in', async () => { + // WHEN + const cx = await builderFixture(toolkit, 'stack-with-role'); + await toolkit.deploy(cx, { + parameters: StackParameters.exactly({ + 'my-param': 'my-value', + }), + }); + + // passed through correctly to Deployments + expect(mockDeployStack).toHaveBeenCalledWith(expect.objectContaining({ + parameters: { 'my-param': 'my-value' }, + })); + + successfulDeployment(); + }); + + test('notification arns are passed in', async () => { + // WHEN + const arn = 'arn:aws:sns:us-east-1:1111111111:resource'; + const cx = await builderFixture(toolkit, 'stack-with-role'); + await toolkit.deploy(cx, { + notificationArns: [arn], + }); + + // passed through correctly to Deployments + expect(mockDeployStack).toHaveBeenCalledWith(expect.objectContaining({ + notificationArns: [arn], + })); + + successfulDeployment(); + }); + + test('notification arns from stack are passed in', async () => { + // WHEN + const arn = 'arn:aws:sns:us-east-1:222222222222:resource'; + const cx = await builderFixture(toolkit, 'stack-with-notification-arns'); + await toolkit.deploy(cx, { + notificationArns: [arn], + }); + + // passed through correctly to Deployments + expect(mockDeployStack).toHaveBeenCalledWith(expect.objectContaining({ + notificationArns: [ + arn, + 'arn:aws:sns:us-east-1:1111111111:resource', + 'arn:aws:sns:us-east-1:1111111111:other-resource', + ], + })); + + successfulDeployment(); + }); + + test('non sns notification arn results in error', async () => { + // WHEN + const arn = 'arn:aws:sqs:us-east-1:1111111111:resource'; + const cx = await builderFixture(toolkit, 'stack-with-role'); + await expect(async () => toolkit.deploy(cx, { + notificationArns: [arn], + })).rejects.toThrow(/Notification arn arn:aws:sqs:us-east-1:1111111111:resource is not a valid arn for an SNS topic/); + }); + }); + + describe('deployment results', () => { + test('did-deploy-result', async () => { + // WHEN + const cx = await builderFixture(toolkit, 'stack-with-role'); + await toolkit.deploy(cx); + + // THEN + successfulDeployment(); + }); + + test('failpaused-need-rollback-first', async () => { + // GIVEN + mockDeployStack.mockImplementation((params) => { + if (params.rollback === true) { + return { + type: 'did-deploy-stack', + stackArn: 'arn:aws:cloudformation:region:account:stack/test-stack', + outputs: {}, + noOp: false, + }; + } else { + return { + type: 'failpaused-need-rollback-first', + stackArn: 'arn:aws:cloudformation:region:account:stack/test-stack', + outputs: {}, + noOp: false, + }; + } + }); + + // WHEN + const cx = await builderFixture(toolkit, 'stack-with-role'); + await toolkit.deploy(cx); + + // THEN + successfulDeployment(); + }); + + test('replacement-requires-rollback', async () => { + // GIVEN + mockDeployStack.mockImplementation((params) => { + if (params.rollback === true) { + return { + type: 'did-deploy-stack', + stackArn: 'arn:aws:cloudformation:region:account:stack/test-stack', + outputs: {}, + noOp: false, + }; + } else { + return { + type: 'replacement-requires-rollback', + stackArn: 'arn:aws:cloudformation:region:account:stack/test-stack', + outputs: {}, + noOp: false, + }; + } + }); + + // WHEN + const cx = await builderFixture(toolkit, 'stack-with-role'); + await toolkit.deploy(cx); + + // THEN + successfulDeployment(); + }); + }); }); + +function successfulDeployment() { + expect(ioHost.notifySpy).toHaveBeenCalledWith(expect.objectContaining({ + action: 'deploy', + level: 'info', + message: expect.stringContaining('Deployment time:'), + })); +}