aws-cdk
Version:
CDK Toolkit, the command line tool for CDK apps
770 lines • 92.1 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/* eslint-disable import/order */
const api_1 = require("../../lib/api");
const common_1 = require("../../lib/api/hotswap/common");
const hotswap_deployments_1 = require("../../lib/api/hotswap-deployments");
const logging_1 = require("../../lib/logging");
const util_1 = require("../util");
const mock_sdk_1 = require("../util/mock-sdk");
const environment_resources_1 = require("../../lib/api/environment-resources");
jest.mock('../../lib/api/hotswap-deployments');
const FAKE_STACK = (0, util_1.testStack)({
stackName: 'withouterrors',
});
const FAKE_STACK_WITH_PARAMETERS = (0, util_1.testStack)({
stackName: 'withparameters',
template: {
Parameters: {
HasValue: { Type: 'String' },
HasDefault: { Type: 'String', Default: 'TheDefault' },
OtherParameter: { Type: 'String' },
},
},
});
const FAKE_STACK_TERMINATION_PROTECTION = (0, util_1.testStack)({
stackName: 'termination-protection',
template: util_1.DEFAULT_FAKE_TEMPLATE,
terminationProtection: true,
});
let sdk;
let sdkProvider;
let cfnMocks;
let stderrMock;
let stdoutMock;
beforeEach(() => {
jest.resetAllMocks();
stderrMock = jest.spyOn(process.stderr, 'write').mockImplementation(() => { return true; });
stdoutMock = jest.spyOn(process.stdout, 'write').mockImplementation(() => { return true; });
sdkProvider = new mock_sdk_1.MockSdkProvider();
sdk = new mock_sdk_1.MockSdk();
cfnMocks = {
describeStackEvents: jest.fn().mockReturnValue({}),
describeStacks: jest.fn()
// First call, no stacks exist
.mockImplementationOnce(() => ({ Stacks: [] }))
// Second call, stack has been created
.mockImplementationOnce(() => ({
Stacks: [
{
StackStatus: 'CREATE_COMPLETE',
StackStatusReason: 'It is magic',
EnableTerminationProtection: false,
},
],
})),
createChangeSet: jest.fn((_o) => ({})),
deleteChangeSet: jest.fn((_o) => ({})),
updateStack: jest.fn((_o) => ({})),
createStack: jest.fn((_o) => ({})),
describeChangeSet: jest.fn((_o) => ({
Status: 'CREATE_COMPLETE',
Changes: [],
})),
executeChangeSet: jest.fn((_o) => ({})),
deleteStack: jest.fn((_o) => ({})),
getTemplate: jest.fn((_o) => ({ TemplateBody: JSON.stringify(util_1.DEFAULT_FAKE_TEMPLATE) })),
updateTerminationProtection: jest.fn((_o) => ({ StackId: 'stack-id' })),
};
sdk.stubCloudFormation(cfnMocks);
sdk.stubGetEndpointSuffix(() => 'amazonaws.com');
});
function standardDeployStackArguments() {
const resolvedEnvironment = (0, mock_sdk_1.mockResolvedEnvironment)();
return {
stack: FAKE_STACK,
sdk,
sdkProvider,
resolvedEnvironment,
envResources: new environment_resources_1.NoBootstrapStackEnvironmentResources(resolvedEnvironment, sdk),
};
}
test("calls tryHotswapDeployment() if 'hotswap' is `HotswapMode.CLASSIC`", async () => {
// WHEN
await (0, api_1.deployStack)({
...standardDeployStackArguments(),
hotswap: common_1.HotswapMode.FALL_BACK,
extraUserAgent: 'extra-user-agent',
});
// THEN
expect(hotswap_deployments_1.tryHotswapDeployment).toHaveBeenCalled();
// check that the extra User-Agent is honored
expect(sdk.appendCustomUserAgent).toHaveBeenCalledWith('extra-user-agent');
// check that the fallback has been called if hotswapping failed
expect(sdk.appendCustomUserAgent).toHaveBeenCalledWith('cdk-hotswap/fallback');
});
test("calls tryHotswapDeployment() if 'hotswap' is `HotswapMode.HOTSWAP_ONLY`", async () => {
cfnMocks.describeStacks = jest.fn()
// we need the first call to return something in the Stacks prop,
// otherwise the access to `stackId` will fail
.mockImplementation(() => ({
Stacks: [
{
StackStatus: 'CREATE_COMPLETE',
StackStatusReason: 'It is magic',
EnableTerminationProtection: false,
},
],
}));
sdk.stubCloudFormation(cfnMocks);
// WHEN
const deployStackResult = await (0, api_1.deployStack)({
...standardDeployStackArguments(),
hotswap: common_1.HotswapMode.HOTSWAP_ONLY,
extraUserAgent: 'extra-user-agent',
force: true, // otherwise, deployment would be skipped
});
// THEN
expect(deployStackResult.noOp).toEqual(true);
expect(hotswap_deployments_1.tryHotswapDeployment).toHaveBeenCalled();
// check that the extra User-Agent is honored
expect(sdk.appendCustomUserAgent).toHaveBeenCalledWith('extra-user-agent');
// check that the fallback has not been called if hotswapping failed
expect(sdk.appendCustomUserAgent).not.toHaveBeenCalledWith('cdk-hotswap/fallback');
});
test('correctly passes CFN parameters when hotswapping', async () => {
// WHEN
await (0, api_1.deployStack)({
...standardDeployStackArguments(),
hotswap: common_1.HotswapMode.FALL_BACK,
parameters: {
A: 'A-value',
B: 'B=value',
C: undefined,
D: '',
},
});
// THEN
expect(hotswap_deployments_1.tryHotswapDeployment).toHaveBeenCalledWith(expect.anything(), { A: 'A-value', B: 'B=value' }, expect.anything(), expect.anything(), common_1.HotswapMode.FALL_BACK);
});
test('correctly passes SSM parameters when hotswapping', async () => {
// GIVEN
givenStackExists({
Parameters: [
{ ParameterKey: 'SomeParameter', ParameterValue: 'ParameterName', ResolvedValue: 'SomeValue' },
],
});
// WHEN
await (0, api_1.deployStack)({
...standardDeployStackArguments(),
stack: (0, util_1.testStack)({
stackName: 'stack',
template: {
Parameters: {
SomeParameter: {
Type: 'AWS::SSM::Parameter::Value<String>',
Default: 'ParameterName',
},
},
},
}),
hotswap: common_1.HotswapMode.FALL_BACK,
usePreviousParameters: true,
});
// THEN
expect(hotswap_deployments_1.tryHotswapDeployment).toHaveBeenCalledWith(expect.anything(), { SomeParameter: 'SomeValue' }, expect.anything(), expect.anything(), common_1.HotswapMode.FALL_BACK);
});
test('call CreateStack when method=direct and the stack doesnt exist yet', async () => {
// WHEN
await (0, api_1.deployStack)({
...standardDeployStackArguments(),
deploymentMethod: { method: 'direct' },
});
// THEN
expect(cfnMocks.createStack).toHaveBeenCalled();
});
test('call UpdateStack when method=direct and the stack exists already', async () => {
// WHEN
givenStackExists();
await (0, api_1.deployStack)({
...standardDeployStackArguments(),
deploymentMethod: { method: 'direct' },
force: true,
});
// THEN
expect(cfnMocks.updateStack).toHaveBeenCalled();
});
test("does not call tryHotswapDeployment() if 'hotswap' is false", async () => {
// WHEN
await (0, api_1.deployStack)({
...standardDeployStackArguments(),
hotswap: undefined,
});
// THEN
expect(hotswap_deployments_1.tryHotswapDeployment).not.toHaveBeenCalled();
});
test("rollback still defaults to enabled even if 'hotswap' is enabled", async () => {
// WHEN
await (0, api_1.deployStack)({
...standardDeployStackArguments(),
hotswap: common_1.HotswapMode.FALL_BACK,
rollback: undefined,
});
// THEN
expect(cfnMocks.executeChangeSet).not.toHaveBeenCalledWith(expect.objectContaining({
DisableRollback: true,
}));
});
test("rollback defaults to enabled if 'hotswap' is undefined", async () => {
// WHEN
await (0, api_1.deployStack)({
...standardDeployStackArguments(),
hotswap: undefined,
rollback: undefined,
});
// THEN
expect(cfnMocks.executeChangeSet).toHaveBeenCalledTimes(1);
expect(cfnMocks.executeChangeSet).not.toHaveBeenCalledWith(expect.objectContaining({
DisableRollback: expect.anything(),
}));
});
test('do deploy executable change set with 0 changes', async () => {
// WHEN
const ret = await (0, api_1.deployStack)({
...standardDeployStackArguments(),
});
// THEN
expect(ret.noOp).toBeFalsy();
expect(cfnMocks.executeChangeSet).toHaveBeenCalled();
});
test('correctly passes CFN parameters, ignoring ones with empty values', async () => {
// WHEN
await (0, api_1.deployStack)({
...standardDeployStackArguments(),
parameters: {
A: 'A-value',
B: 'B=value',
C: undefined,
D: '',
},
});
// THEN
expect(cfnMocks.createChangeSet).toHaveBeenCalledWith(expect.objectContaining({
Parameters: [
{ ParameterKey: 'A', ParameterValue: 'A-value' },
{ ParameterKey: 'B', ParameterValue: 'B=value' },
],
}));
});
test('reuse previous parameters if requested', async () => {
// GIVEN
givenStackExists({
Parameters: [
{ ParameterKey: 'HasValue', ParameterValue: 'TheValue' },
{ ParameterKey: 'HasDefault', ParameterValue: 'TheOldValue' },
],
});
// WHEN
await (0, api_1.deployStack)({
...standardDeployStackArguments(),
stack: FAKE_STACK_WITH_PARAMETERS,
parameters: {
OtherParameter: 'SomeValue',
},
usePreviousParameters: true,
});
// THEN
expect(cfnMocks.createChangeSet).toHaveBeenCalledWith(expect.objectContaining({
Parameters: [
{ ParameterKey: 'HasValue', UsePreviousValue: true },
{ ParameterKey: 'HasDefault', UsePreviousValue: true },
{ ParameterKey: 'OtherParameter', ParameterValue: 'SomeValue' },
],
}));
});
describe('ci=true', () => {
beforeEach(() => {
(0, logging_1.setCI)(true);
});
afterEach(() => {
(0, logging_1.setCI)(false);
});
test('output written to stdout', async () => {
// GIVEN
await (0, api_1.deployStack)({
...standardDeployStackArguments(),
});
// THEN
expect(stderrMock.mock.calls).toEqual([]);
expect(stdoutMock.mock.calls).not.toEqual([]);
});
});
test('do not reuse previous parameters if not requested', async () => {
// GIVEN
givenStackExists({
Parameters: [
{ ParameterKey: 'HasValue', ParameterValue: 'TheValue' },
{ ParameterKey: 'HasDefault', ParameterValue: 'TheOldValue' },
],
});
// WHEN
await (0, api_1.deployStack)({
...standardDeployStackArguments(),
stack: FAKE_STACK_WITH_PARAMETERS,
parameters: {
HasValue: 'SomeValue',
OtherParameter: 'SomeValue',
},
});
// THEN
expect(cfnMocks.createChangeSet).toHaveBeenCalledWith(expect.objectContaining({
Parameters: [
{ ParameterKey: 'HasValue', ParameterValue: 'SomeValue' },
{ ParameterKey: 'OtherParameter', ParameterValue: 'SomeValue' },
],
}));
});
test('throw exception if not enough parameters supplied', async () => {
// GIVEN
givenStackExists({
Parameters: [
{ ParameterKey: 'HasValue', ParameterValue: 'TheValue' },
{ ParameterKey: 'HasDefault', ParameterValue: 'TheOldValue' },
],
});
// WHEN
await expect((0, api_1.deployStack)({
...standardDeployStackArguments(),
stack: FAKE_STACK_WITH_PARAMETERS,
parameters: {
OtherParameter: 'SomeValue',
},
})).rejects.toThrow(/CloudFormation Parameters are missing a value/);
});
test('deploy is skipped if template did not change', async () => {
// GIVEN
givenStackExists();
// WHEN
await (0, api_1.deployStack)({
...standardDeployStackArguments(),
});
// THEN
expect(cfnMocks.executeChangeSet).not.toBeCalled();
});
test('deploy is skipped if parameters are the same', async () => {
// GIVEN
givenTemplateIs(FAKE_STACK_WITH_PARAMETERS.template);
givenStackExists({
Parameters: [
{ ParameterKey: 'HasValue', ParameterValue: 'HasValue' },
{ ParameterKey: 'HasDefault', ParameterValue: 'HasDefault' },
{ ParameterKey: 'OtherParameter', ParameterValue: 'OtherParameter' },
],
});
// WHEN
await (0, api_1.deployStack)({
...standardDeployStackArguments(),
stack: FAKE_STACK_WITH_PARAMETERS,
parameters: {},
usePreviousParameters: true,
});
// THEN
expect(cfnMocks.createChangeSet).not.toHaveBeenCalled();
});
test('deploy is not skipped if parameters are different', async () => {
// GIVEN
givenTemplateIs(FAKE_STACK_WITH_PARAMETERS.template);
givenStackExists({
Parameters: [
{ ParameterKey: 'HasValue', ParameterValue: 'HasValue' },
{ ParameterKey: 'HasDefault', ParameterValue: 'HasDefault' },
{ ParameterKey: 'OtherParameter', ParameterValue: 'OtherParameter' },
],
});
// WHEN
await (0, api_1.deployStack)({
...standardDeployStackArguments(),
stack: FAKE_STACK_WITH_PARAMETERS,
parameters: {
HasValue: 'NewValue',
},
usePreviousParameters: true,
});
// THEN
expect(cfnMocks.createChangeSet).toHaveBeenCalledWith(expect.objectContaining({
Parameters: [
{ ParameterKey: 'HasValue', ParameterValue: 'NewValue' },
{ ParameterKey: 'HasDefault', UsePreviousValue: true },
{ ParameterKey: 'OtherParameter', UsePreviousValue: true },
],
}));
});
test('if existing stack failed to create, it is deleted and recreated', async () => {
// GIVEN
givenStackExists({ StackStatus: 'ROLLBACK_COMPLETE' }, // This is for the initial check
{ StackStatus: 'DELETE_COMPLETE' }, // Poll the successful deletion
{ StackStatus: 'CREATE_COMPLETE' });
givenTemplateIs({
DifferentThan: 'TheDefault',
});
// WHEN
await (0, api_1.deployStack)({
...standardDeployStackArguments(),
});
// THEN
expect(cfnMocks.deleteStack).toHaveBeenCalled();
expect(cfnMocks.createChangeSet).toHaveBeenCalledWith(expect.objectContaining({
ChangeSetType: 'CREATE',
}));
});
test('if existing stack failed to create, it is deleted and recreated even if the template did not change', async () => {
// GIVEN
givenStackExists({ StackStatus: 'ROLLBACK_COMPLETE' }, // This is for the initial check
{ StackStatus: 'DELETE_COMPLETE' }, // Poll the successful deletion
{ StackStatus: 'CREATE_COMPLETE' });
// WHEN
await (0, api_1.deployStack)({
...standardDeployStackArguments(),
});
// THEN
expect(cfnMocks.deleteStack).toHaveBeenCalled();
expect(cfnMocks.createChangeSet).toHaveBeenCalledWith(expect.objectContaining({
ChangeSetType: 'CREATE',
}));
});
test('deploy not skipped if template did not change and --force is applied', async () => {
// GIVEN
givenStackExists();
// WHEN
await (0, api_1.deployStack)({
...standardDeployStackArguments(),
force: true,
});
// THEN
expect(cfnMocks.executeChangeSet).toHaveBeenCalled();
});
test('deploy is skipped if template and tags did not change', async () => {
// GIVEN
givenStackExists({
Tags: [
{ Key: 'Key1', Value: 'Value1' },
{ Key: 'Key2', Value: 'Value2' },
],
});
// WHEN
await (0, api_1.deployStack)({
...standardDeployStackArguments(),
tags: [
{ Key: 'Key1', Value: 'Value1' },
{ Key: 'Key2', Value: 'Value2' },
],
});
// THEN
expect(cfnMocks.createChangeSet).not.toBeCalled();
expect(cfnMocks.executeChangeSet).not.toBeCalled();
expect(cfnMocks.describeStacks).toHaveBeenCalledWith({ StackName: 'withouterrors' });
expect(cfnMocks.getTemplate).toHaveBeenCalledWith({ StackName: 'withouterrors', TemplateStage: 'Original' });
});
test('deploy not skipped if template did not change but tags changed', async () => {
// GIVEN
givenStackExists({
Tags: [
{ Key: 'Key', Value: 'Value' },
],
});
// WHEN
const resolvedEnvironment = (0, mock_sdk_1.mockResolvedEnvironment)();
await (0, api_1.deployStack)({
stack: FAKE_STACK,
sdk,
sdkProvider,
resolvedEnvironment,
tags: [
{
Key: 'Key',
Value: 'NewValue',
},
],
envResources: new environment_resources_1.NoBootstrapStackEnvironmentResources(resolvedEnvironment, sdk),
});
// THEN
expect(cfnMocks.createChangeSet).toHaveBeenCalled();
expect(cfnMocks.executeChangeSet).toHaveBeenCalled();
expect(cfnMocks.describeChangeSet).toHaveBeenCalled();
expect(cfnMocks.describeStacks).toHaveBeenCalledWith({ StackName: 'withouterrors' });
expect(cfnMocks.getTemplate).toHaveBeenCalledWith({ StackName: 'withouterrors', TemplateStage: 'Original' });
});
test('deployStack reports no change if describeChangeSet returns specific error', async () => {
cfnMocks.describeChangeSet?.mockImplementation(() => ({
Status: 'FAILED',
StatusReason: 'No updates are to be performed.',
}));
// WHEN
const deployResult = await (0, api_1.deployStack)({
...standardDeployStackArguments(),
});
// THEN
expect(deployResult.noOp).toEqual(true);
});
test('deploy not skipped if template did not change but one tag removed', async () => {
// GIVEN
givenStackExists({
Tags: [
{ Key: 'Key1', Value: 'Value1' },
{ Key: 'Key2', Value: 'Value2' },
],
});
// WHEN
await (0, api_1.deployStack)({
...standardDeployStackArguments(),
tags: [
{ Key: 'Key1', Value: 'Value1' },
],
});
// THEN
expect(cfnMocks.createChangeSet).toHaveBeenCalled();
expect(cfnMocks.executeChangeSet).toHaveBeenCalled();
expect(cfnMocks.describeChangeSet).toHaveBeenCalled();
expect(cfnMocks.describeStacks).toHaveBeenCalledWith({ StackName: 'withouterrors' });
expect(cfnMocks.getTemplate).toHaveBeenCalledWith({ StackName: 'withouterrors', TemplateStage: 'Original' });
});
test('deploy is not skipped if stack is in a _FAILED state', async () => {
// GIVEN
givenStackExists({
StackStatus: 'DELETE_FAILED',
});
// WHEN
await (0, api_1.deployStack)({
...standardDeployStackArguments(),
usePreviousParameters: true,
}).catch(() => { });
// THEN
expect(cfnMocks.createChangeSet).toHaveBeenCalled();
});
test('existing stack in UPDATE_ROLLBACK_COMPLETE state can be updated', async () => {
// GIVEN
givenStackExists({ StackStatus: 'UPDATE_ROLLBACK_COMPLETE' }, // This is for the initial check
{ StackStatus: 'UPDATE_COMPLETE' });
givenTemplateIs({ changed: 123 });
// WHEN
await (0, api_1.deployStack)({
...standardDeployStackArguments(),
});
// THEN
expect(cfnMocks.deleteStack).not.toHaveBeenCalled();
expect(cfnMocks.createChangeSet).toHaveBeenCalledWith(expect.objectContaining({
ChangeSetType: 'UPDATE',
}));
});
test('deploy not skipped if template changed', async () => {
// GIVEN
givenStackExists();
givenTemplateIs({ changed: 123 });
// WHEN
await (0, api_1.deployStack)({
...standardDeployStackArguments(),
});
// THEN
expect(cfnMocks.executeChangeSet).toHaveBeenCalled();
});
test('not executed and no error if --no-execute is given', async () => {
// WHEN
await (0, api_1.deployStack)({
...standardDeployStackArguments(),
deploymentMethod: { method: 'change-set', execute: false },
});
// THEN
expect(cfnMocks.executeChangeSet).not.toHaveBeenCalled();
});
test('empty change set is deleted if --execute is given', async () => {
cfnMocks.describeChangeSet?.mockImplementation(() => ({
Status: 'FAILED',
StatusReason: 'No updates are to be performed.',
}));
// GIVEN
givenStackExists();
// WHEN
await (0, api_1.deployStack)({
...standardDeployStackArguments(),
deploymentMethod: { method: 'change-set', execute: true },
force: true, // Necessary to bypass "skip deploy"
});
// THEN
expect(cfnMocks.createChangeSet).toHaveBeenCalled();
expect(cfnMocks.executeChangeSet).not.toHaveBeenCalled();
//the first deletion is for any existing cdk change sets, the second is for the deleting the new empty change set
expect(cfnMocks.deleteChangeSet).toHaveBeenCalledTimes(2);
});
test('empty change set is not deleted if --no-execute is given', async () => {
cfnMocks.describeChangeSet?.mockImplementation(() => ({
Status: 'FAILED',
StatusReason: 'No updates are to be performed.',
}));
// GIVEN
givenStackExists();
// WHEN
await (0, api_1.deployStack)({
...standardDeployStackArguments(),
deploymentMethod: { method: 'change-set', execute: false },
});
// THEN
expect(cfnMocks.createChangeSet).toHaveBeenCalled();
expect(cfnMocks.executeChangeSet).not.toHaveBeenCalled();
//the first deletion is for any existing cdk change sets
expect(cfnMocks.deleteChangeSet).toHaveBeenCalledTimes(1);
});
test('use S3 url for stack deployment if present in Stack Artifact', async () => {
// WHEN
await (0, api_1.deployStack)({
...standardDeployStackArguments(),
stack: (0, util_1.testStack)({
stackName: 'withouterrors',
properties: {
stackTemplateAssetObjectUrl: 'https://use-me-use-me/',
},
}),
});
// THEN
expect(cfnMocks.createChangeSet).toHaveBeenCalledWith(expect.objectContaining({
TemplateURL: 'https://use-me-use-me/',
}));
expect(cfnMocks.executeChangeSet).toHaveBeenCalled();
});
test('use REST API S3 url with substituted placeholders if manifest url starts with s3://', async () => {
// WHEN
await (0, api_1.deployStack)({
...standardDeployStackArguments(),
stack: (0, util_1.testStack)({
stackName: 'withouterrors',
properties: {
stackTemplateAssetObjectUrl: 's3://use-me-use-me-${AWS::AccountId}/object',
},
}),
});
// THEN
expect(cfnMocks.createChangeSet).toHaveBeenCalledWith(expect.objectContaining({
TemplateURL: 'https://s3.bermuda-triangle-1337.amazonaws.com/use-me-use-me-123456789/object',
}));
expect(cfnMocks.executeChangeSet).toHaveBeenCalled();
});
test('changeset is created when stack exists in REVIEW_IN_PROGRESS status', async () => {
// GIVEN
givenStackExists({
StackStatus: 'REVIEW_IN_PROGRESS',
Tags: [
{ Key: 'Key1', Value: 'Value1' },
{ Key: 'Key2', Value: 'Value2' },
],
});
// WHEN
await (0, api_1.deployStack)({
...standardDeployStackArguments(),
deploymentMethod: { method: 'change-set', execute: false },
});
// THEN
expect(cfnMocks.createChangeSet).toHaveBeenCalledWith(expect.objectContaining({
ChangeSetType: 'CREATE',
StackName: 'withouterrors',
}));
expect(cfnMocks.executeChangeSet).not.toHaveBeenCalled();
});
test('changeset is updated when stack exists in CREATE_COMPLETE status', async () => {
// GIVEN
givenStackExists({
Tags: [
{ Key: 'Key1', Value: 'Value1' },
{ Key: 'Key2', Value: 'Value2' },
],
});
// WHEN
await (0, api_1.deployStack)({
...standardDeployStackArguments(),
deploymentMethod: { method: 'change-set', execute: false },
});
// THEN
expect(cfnMocks.createChangeSet).toHaveBeenCalledWith(expect.objectContaining({
ChangeSetType: 'UPDATE',
StackName: 'withouterrors',
}));
expect(cfnMocks.executeChangeSet).not.toHaveBeenCalled();
});
test('deploy with termination protection enabled', async () => {
// WHEN
await (0, api_1.deployStack)({
...standardDeployStackArguments(),
stack: FAKE_STACK_TERMINATION_PROTECTION,
});
// THEN
expect(cfnMocks.updateTerminationProtection).toHaveBeenCalledWith(expect.objectContaining({
EnableTerminationProtection: true,
}));
});
test('updateTerminationProtection not called when termination protection is undefined', async () => {
// WHEN
await (0, api_1.deployStack)({
...standardDeployStackArguments(),
});
// THEN
expect(cfnMocks.updateTerminationProtection).not.toHaveBeenCalled();
});
test('updateTerminationProtection called when termination protection is undefined and stack has termination protection', async () => {
// GIVEN
givenStackExists({
EnableTerminationProtection: true,
});
// WHEN
await (0, api_1.deployStack)({
...standardDeployStackArguments(),
});
// THEN
expect(cfnMocks.updateTerminationProtection).toHaveBeenCalledWith(expect.objectContaining({
EnableTerminationProtection: false,
}));
});
describe('disable rollback', () => {
test('by default, we do not disable rollback (and also do not pass the flag)', async () => {
// WHEN
await (0, api_1.deployStack)({
...standardDeployStackArguments(),
});
// THEN
expect(cfnMocks.executeChangeSet).toHaveBeenCalledTimes(1);
expect(cfnMocks.executeChangeSet).not.toHaveBeenCalledWith(expect.objectContaining({
DisableRollback: expect.anything(),
}));
});
test('rollback can be disabled by setting rollback: false', async () => {
// WHEN
await (0, api_1.deployStack)({
...standardDeployStackArguments(),
rollback: false,
});
// THEN
expect(cfnMocks.executeChangeSet).toHaveBeenCalledWith(expect.objectContaining({
DisableRollback: true,
}));
});
});
/**
* Set up the mocks so that it looks like the stack exists to start with
*
* The last element of this array will be continuously repeated.
*/
function givenStackExists(...overrides) {
cfnMocks.describeStacks.mockReset();
if (overrides.length === 0) {
overrides = [{}];
}
const baseResponse = {
StackName: 'mock-stack-name',
StackId: 'mock-stack-id',
CreationTime: new Date(),
StackStatus: 'CREATE_COMPLETE',
EnableTerminationProtection: false,
};
for (const override of overrides.slice(0, overrides.length - 1)) {
cfnMocks.describeStacks.mockImplementationOnce(() => ({
Stacks: [{ ...baseResponse, ...override }],
}));
}
cfnMocks.describeStacks.mockImplementation(() => ({
Stacks: [{ ...baseResponse, ...overrides[overrides.length - 1] }],
}));
}
function givenTemplateIs(template) {
cfnMocks.getTemplate.mockReset();
cfnMocks.getTemplate.mockReturnValue({
TemplateBody: JSON.stringify(template),
});
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVwbG95LXN0YWNrLnRlc3QuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJkZXBsb3ktc3RhY2sudGVzdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLGlDQUFpQztBQUNqQyx1Q0FBZ0U7QUFDaEUseURBQTJEO0FBQzNELDJFQUF5RTtBQUN6RSwrQ0FBMEM7QUFDMUMsa0NBQTJEO0FBQzNELCtDQUF3SDtBQUN4SCwrRUFBMkY7QUFFM0YsSUFBSSxDQUFDLElBQUksQ0FBQyxtQ0FBbUMsQ0FBQyxDQUFDO0FBRS9DLE1BQU0sVUFBVSxHQUFHLElBQUEsZ0JBQVMsRUFBQztJQUMzQixTQUFTLEVBQUUsZUFBZTtDQUMzQixDQUFDLENBQUM7QUFFSCxNQUFNLDBCQUEwQixHQUFHLElBQUEsZ0JBQVMsRUFBQztJQUMzQyxTQUFTLEVBQUUsZ0JBQWdCO0lBQzNCLFFBQVEsRUFBRTtRQUNSLFVBQVUsRUFBRTtZQUNWLFFBQVEsRUFBRSxFQUFFLElBQUksRUFBRSxRQUFRLEVBQUU7WUFDNUIsVUFBVSxFQUFFLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRSxPQUFPLEVBQUUsWUFBWSxFQUFFO1lBQ3JELGNBQWMsRUFBRSxFQUFFLElBQUksRUFBRSxRQUFRLEVBQUU7U0FDbkM7S0FDRjtDQUNGLENBQUMsQ0FBQztBQUVILE1BQU0saUNBQWlDLEdBQUcsSUFBQSxnQkFBUyxFQUFDO0lBQ2xELFNBQVMsRUFBRSx3QkFBd0I7SUFDbkMsUUFBUSxFQUFFLDRCQUFxQjtJQUMvQixxQkFBcUIsRUFBRSxJQUFJO0NBQzVCLENBQUMsQ0FBQztBQUVILElBQUksR0FBWSxDQUFDO0FBQ2pCLElBQUksV0FBNEIsQ0FBQztBQUNqQyxJQUFJLFFBQStELENBQUM7QUFDcEUsSUFBSSxVQUE0QixDQUFDO0FBQ2pDLElBQUksVUFBNEIsQ0FBQztBQUVqQyxVQUFVLENBQUMsR0FBRyxFQUFFO0lBQ2QsSUFBSSxDQUFDLGFBQWEsRUFBRSxDQUFDO0lBQ3JCLFVBQVUsR0FB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