UNPKG

aws-cdk

Version:

CDK Toolkit, the command line tool for CDK apps

770 lines 92.1 kB
"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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