UNPKG

aws-cdk

Version:

CDK Toolkit, the command line tool for CDK apps

980 lines 108 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const setup = require("./hotswap-test-setup"); const common_1 = require("../../../lib/api/hotswap/common"); let mockUpdateLambdaCode; let mockUpdateLambdaConfiguration; let mockMakeRequest; let hotswapMockSdkProvider; beforeEach(() => { hotswapMockSdkProvider = setup.setupHotswapTests(); mockUpdateLambdaCode = jest.fn().mockReturnValue({}); mockUpdateLambdaConfiguration = jest.fn().mockReturnValue({}); mockMakeRequest = jest.fn().mockReturnValue({ promise: () => Promise.resolve({}), response: {}, addListeners: () => { }, }); hotswapMockSdkProvider.stubLambda({ updateFunctionCode: mockUpdateLambdaCode, updateFunctionConfiguration: mockUpdateLambdaConfiguration, }, { makeRequest: mockMakeRequest, }); }); describe.each([common_1.HotswapMode.FALL_BACK, common_1.HotswapMode.HOTSWAP_ONLY])('%p mode', (hotswapMode) => { test('returns undefined when a new Lambda function is added to the Stack', async () => { // GIVEN const cdkStackArtifact = setup.cdkStackArtifactOf({ template: { Resources: { Func: { Type: 'AWS::Lambda::Function', }, }, }, }); if (hotswapMode === common_1.HotswapMode.FALL_BACK) { // WHEN const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact); // THEN expect(deployStackResult).toBeUndefined(); } else if (hotswapMode === common_1.HotswapMode.HOTSWAP_ONLY) { // WHEN const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact); // THEN expect(deployStackResult).not.toBeUndefined(); expect(deployStackResult?.noOp).toEqual(true); expect(mockUpdateLambdaCode).not.toHaveBeenCalled(); } }); test('calls the updateLambdaCode() API when it receives only a code difference in a Lambda function', async () => { // GIVEN setup.setCurrentCfnStackTemplate({ Resources: { Func: { Type: 'AWS::Lambda::Function', Properties: { Code: { S3Bucket: 'current-bucket', S3Key: 'current-key', }, FunctionName: 'my-function', }, Metadata: { 'aws:asset:path': 'old-path', }, }, }, }); const cdkStackArtifact = setup.cdkStackArtifactOf({ template: { Resources: { Func: { Type: 'AWS::Lambda::Function', Properties: { Code: { S3Bucket: 'current-bucket', S3Key: 'new-key', }, FunctionName: 'my-function', }, Metadata: { 'aws:asset:path': 'new-path', }, }, }, }, }); // WHEN const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact); // THEN expect(deployStackResult).not.toBeUndefined(); expect(mockUpdateLambdaCode).toHaveBeenCalledWith({ FunctionName: 'my-function', S3Bucket: 'current-bucket', S3Key: 'new-key', }); }); test("correctly evaluates the function's name when it references a different resource from the template", async () => { // GIVEN setup.setCurrentCfnStackTemplate({ Resources: { Bucket: { Type: 'AWS::S3::Bucket', }, Func: { Type: 'AWS::Lambda::Function', Properties: { Code: { S3Bucket: 'current-bucket', S3Key: 'current-key', }, FunctionName: { 'Fn::Join': ['-', [ 'lambda', { Ref: 'Bucket' }, 'function', ]], }, }, Metadata: { 'aws:asset:path': 'old-path', }, }, }, }); setup.pushStackResourceSummaries(setup.stackSummaryOf('Bucket', 'AWS::S3::Bucket', 'mybucket')); const cdkStackArtifact = setup.cdkStackArtifactOf({ template: { Resources: { Bucket: { Type: 'AWS::S3::Bucket', }, Func: { Type: 'AWS::Lambda::Function', Properties: { Code: { S3Bucket: 'current-bucket', S3Key: 'new-key', }, FunctionName: { 'Fn::Join': ['-', [ 'lambda', { Ref: 'Bucket' }, 'function', ]], }, }, Metadata: { 'aws:asset:path': 'old-path', }, }, }, }, }); // WHEN const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact); // THEN expect(deployStackResult).not.toBeUndefined(); expect(mockUpdateLambdaCode).toHaveBeenCalledWith({ FunctionName: 'lambda-mybucket-function', S3Bucket: 'current-bucket', S3Key: 'new-key', }); }); test("correctly falls back to taking the function's name from the current stack if it can't evaluate it in the template", async () => { // GIVEN setup.setCurrentCfnStackTemplate({ Parameters: { Param1: { Type: 'String' }, AssetBucketParam: { Type: 'String' }, }, Resources: { Func: { Type: 'AWS::Lambda::Function', Properties: { Code: { S3Bucket: { Ref: 'AssetBucketParam' }, S3Key: 'current-key', }, FunctionName: { Ref: 'Param1' }, }, Metadata: { 'aws:asset:path': 'old-path', }, }, }, }); setup.pushStackResourceSummaries(setup.stackSummaryOf('Func', 'AWS::Lambda::Function', 'my-function')); const cdkStackArtifact = setup.cdkStackArtifactOf({ template: { Parameters: { Param1: { Type: 'String' }, AssetBucketParam: { Type: 'String' }, }, Resources: { Func: { Type: 'AWS::Lambda::Function', Properties: { Code: { S3Bucket: { Ref: 'AssetBucketParam' }, S3Key: 'new-key', }, FunctionName: { Ref: 'Param1' }, }, Metadata: { 'aws:asset:path': 'new-path', }, }, }, }, }); // WHEN const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact, { AssetBucketParam: 'asset-bucket' }); // THEN expect(deployStackResult).not.toBeUndefined(); expect(mockUpdateLambdaCode).toHaveBeenCalledWith({ FunctionName: 'my-function', S3Bucket: 'asset-bucket', S3Key: 'new-key', }); }); test("will not perform a hotswap deployment if it cannot find a Ref target (outside the function's name)", async () => { // GIVEN setup.setCurrentCfnStackTemplate({ Parameters: { Param1: { Type: 'String' }, }, Resources: { Func: { Type: 'AWS::Lambda::Function', Properties: { Code: { S3Bucket: { 'Fn::Sub': '${Param1}' }, S3Key: 'current-key', }, }, Metadata: { 'aws:asset:path': 'old-path', }, }, }, }); setup.pushStackResourceSummaries(setup.stackSummaryOf('Func', 'AWS::Lambda::Function', 'my-func')); const cdkStackArtifact = setup.cdkStackArtifactOf({ template: { Parameters: { Param1: { Type: 'String' }, }, Resources: { Func: { Type: 'AWS::Lambda::Function', Properties: { Code: { S3Bucket: { 'Fn::Sub': '${Param1}' }, S3Key: 'new-key', }, }, Metadata: { 'aws:asset:path': 'new-path', }, }, }, }, }); // THEN await expect(() => hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact)).rejects.toThrow(/Parameter or resource 'Param1' could not be found for evaluation/); }); test("will not perform a hotswap deployment if it doesn't know how to handle a specific attribute (outside the function's name)", async () => { // GIVEN setup.setCurrentCfnStackTemplate({ Resources: { Bucket: { Type: 'AWS::S3::Bucket', }, Func: { Type: 'AWS::Lambda::Function', Properties: { Code: { S3Bucket: { 'Fn::GetAtt': ['Bucket', 'UnknownAttribute'] }, S3Key: 'current-key', }, }, Metadata: { 'aws:asset:path': 'old-path', }, }, }, }); setup.pushStackResourceSummaries(setup.stackSummaryOf('Func', 'AWS::Lambda::Function', 'my-func'), setup.stackSummaryOf('Bucket', 'AWS::S3::Bucket', 'my-bucket')); const cdkStackArtifact = setup.cdkStackArtifactOf({ template: { Resources: { Bucket: { Type: 'AWS::S3::Bucket', }, Func: { Type: 'AWS::Lambda::Function', Properties: { Code: { S3Bucket: { 'Fn::GetAtt': ['Bucket', 'UnknownAttribute'] }, S3Key: 'new-key', }, }, Metadata: { 'aws:asset:path': 'new-path', }, }, }, }, }); // THEN await expect(() => hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact)).rejects.toThrow("We don't support the 'UnknownAttribute' attribute of the 'AWS::S3::Bucket' resource. This is a CDK limitation. Please report it at https://github.com/aws/aws-cdk/issues/new/choose"); }); test('calls the updateLambdaCode() API when it receives a code difference in a Lambda function with no name', async () => { // GIVEN setup.setCurrentCfnStackTemplate({ Resources: { Func: { Type: 'AWS::Lambda::Function', Properties: { Code: { S3Bucket: 'current-bucket', S3Key: 'current-key', }, }, Metadata: { 'aws:asset:path': 'current-path', }, }, }, }); const cdkStackArtifact = setup.cdkStackArtifactOf({ template: { Resources: { Func: { Type: 'AWS::Lambda::Function', Properties: { Code: { S3Bucket: 'current-bucket', S3Key: 'new-key', }, }, Metadata: { 'aws:asset:path': 'current-path', }, }, }, }, }); // WHEN setup.pushStackResourceSummaries(setup.stackSummaryOf('Func', 'AWS::Lambda::Function', 'mock-function-resource-id')); const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact); // THEN expect(deployStackResult).not.toBeUndefined(); expect(mockUpdateLambdaCode).toHaveBeenCalledWith({ FunctionName: 'mock-function-resource-id', S3Bucket: 'current-bucket', S3Key: 'new-key', }); }); test('does not call the updateLambdaCode() API when it receives a change that is not a code difference in a Lambda function', async () => { // GIVEN setup.setCurrentCfnStackTemplate({ Resources: { Func: { Type: 'AWS::Lambda::Function', Properties: { Code: { S3Bucket: 'current-bucket', S3Key: 'current-key', }, PackageType: 'Zip', }, }, }, }); const cdkStackArtifact = setup.cdkStackArtifactOf({ template: { Resources: { Func: { Type: 'AWS::Lambda::Function', Properties: { Code: { S3Bucket: 'current-bucket', S3Key: 'current-key', }, PackageType: 'Image', }, }, }, }, }); if (hotswapMode === common_1.HotswapMode.FALL_BACK) { // WHEN const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact); // THEN expect(deployStackResult).toBeUndefined(); expect(mockUpdateLambdaCode).not.toHaveBeenCalled(); } else if (hotswapMode === common_1.HotswapMode.HOTSWAP_ONLY) { // WHEN const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact); // THEN expect(deployStackResult).not.toBeUndefined(); expect(deployStackResult?.noOp).toEqual(true); expect(mockUpdateLambdaCode).not.toHaveBeenCalled(); } }); test(`when it receives a non-hotswappable change that includes a code difference in a Lambda function, it does not call the updateLambdaCode() API in CLASSIC mode but does in HOTSWAP_ONLY mode`, async () => { // GIVEN setup.setCurrentCfnStackTemplate({ Resources: { Func: { Type: 'AWS::Lambda::Function', Properties: { Code: { S3Bucket: 'current-bucket', S3Key: 'current-key', }, FunctionName: 'my-function', PackageType: 'Zip', }, }, }, }); const cdkStackArtifact = setup.cdkStackArtifactOf({ template: { Resources: { Func: { Type: 'AWS::Lambda::Function', Properties: { Code: { S3Bucket: 'current-bucket', S3Key: 'new-key', }, FunctionName: 'my-function', PackageType: 'Image', }, }, }, }, }); if (hotswapMode === common_1.HotswapMode.FALL_BACK) { // WHEN const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact); // THEN expect(deployStackResult).toBeUndefined(); expect(mockUpdateLambdaCode).not.toHaveBeenCalled(); } else if (hotswapMode === common_1.HotswapMode.HOTSWAP_ONLY) { // WHEN const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact); // THEN expect(deployStackResult).not.toBeUndefined(); expect(mockUpdateLambdaCode).toHaveBeenCalledWith({ FunctionName: 'my-function', S3Bucket: 'current-bucket', S3Key: 'new-key', }); } }); test('does not call the updateLambdaCode() API when a resource with type that is not AWS::Lambda::Function but has the same properties is changed', async () => { // GIVEN setup.setCurrentCfnStackTemplate({ Resources: { Func: { Type: 'AWS::NotLambda::NotAFunction', Properties: { Code: { S3Bucket: 'current-bucket', S3Key: 'current-key', }, }, Metadata: { 'aws:asset:path': 'old-path', }, }, }, }); const cdkStackArtifact = setup.cdkStackArtifactOf({ template: { Resources: { Func: { Type: 'AWS::NotLambda::NotAFunction', Properties: { Code: { S3Bucket: 'current-bucket', S3Key: 'new-key', }, }, Metadata: { 'aws:asset:path': 'old-path', }, }, }, }, }); if (hotswapMode === common_1.HotswapMode.FALL_BACK) { // WHEN const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact); // THEN expect(deployStackResult).toBeUndefined(); expect(mockUpdateLambdaCode).not.toHaveBeenCalled(); } else if (hotswapMode === common_1.HotswapMode.HOTSWAP_ONLY) { // WHEN const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact); // THEN expect(deployStackResult).not.toBeUndefined(); expect(deployStackResult?.noOp).toEqual(true); expect(mockUpdateLambdaCode).not.toHaveBeenCalled(); } }); test('calls getFunction() after function code is updated with delay 1', async () => { // GIVEN setup.setCurrentCfnStackTemplate({ Resources: { Func: { Type: 'AWS::Lambda::Function', Properties: { Code: { S3Bucket: 'current-bucket', S3Key: 'current-key', }, FunctionName: 'my-function', }, Metadata: { 'aws:asset:path': 'old-path', }, }, }, }); const cdkStackArtifact = setup.cdkStackArtifactOf({ template: { Resources: { Func: { Type: 'AWS::Lambda::Function', Properties: { Code: { S3Bucket: 'current-bucket', S3Key: 'new-key', }, FunctionName: 'my-function', }, Metadata: { 'aws:asset:path': 'new-path', }, }, }, }, }); // WHEN await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact); // THEN expect(mockMakeRequest).toHaveBeenCalledWith('getFunction', { FunctionName: 'my-function' }); expect(hotswapMockSdkProvider.getLambdaApiWaiters()).toEqual(expect.objectContaining({ updateFunctionPropertiesToFinish: expect.objectContaining({ name: 'UpdateFunctionPropertiesToFinish', delay: 1, }), })); }); test('calls getFunction() after function code is updated and VpcId is empty string with delay 1', async () => { // GIVEN mockUpdateLambdaCode = jest.fn().mockReturnValue({ VpcConfig: { VpcId: '', }, }); hotswapMockSdkProvider.stubLambda({ updateFunctionCode: mockUpdateLambdaCode, }); setup.setCurrentCfnStackTemplate({ Resources: { Func: { Type: 'AWS::Lambda::Function', Properties: { Code: { S3Bucket: 'current-bucket', S3Key: 'current-key', }, FunctionName: 'my-function', }, Metadata: { 'aws:asset:path': 'old-path', }, }, }, }); const cdkStackArtifact = setup.cdkStackArtifactOf({ template: { Resources: { Func: { Type: 'AWS::Lambda::Function', Properties: { Code: { S3Bucket: 'current-bucket', S3Key: 'new-key', }, FunctionName: 'my-function', }, Metadata: { 'aws:asset:path': 'new-path', }, }, }, }, }); // WHEN await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact); // THEN expect(hotswapMockSdkProvider.getLambdaApiWaiters()).toEqual(expect.objectContaining({ updateFunctionPropertiesToFinish: expect.objectContaining({ name: 'UpdateFunctionPropertiesToFinish', delay: 1, }), })); }); test('calls getFunction() after function code is updated on a VPC function with delay 5', async () => { // GIVEN mockUpdateLambdaCode = jest.fn().mockReturnValue({ VpcConfig: { VpcId: 'abc', }, }); hotswapMockSdkProvider.stubLambda({ updateFunctionCode: mockUpdateLambdaCode, }); setup.setCurrentCfnStackTemplate({ Resources: { Func: { Type: 'AWS::Lambda::Function', Properties: { Code: { S3Bucket: 'current-bucket', S3Key: 'current-key', }, FunctionName: 'my-function', }, Metadata: { 'aws:asset:path': 'old-path', }, }, }, }); const cdkStackArtifact = setup.cdkStackArtifactOf({ template: { Resources: { Func: { Type: 'AWS::Lambda::Function', Properties: { Code: { S3Bucket: 'current-bucket', S3Key: 'new-key', }, FunctionName: 'my-function', }, Metadata: { 'aws:asset:path': 'new-path', }, }, }, }, }); // WHEN await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact); // THEN expect(hotswapMockSdkProvider.getLambdaApiWaiters()).toEqual(expect.objectContaining({ updateFunctionPropertiesToFinish: expect.objectContaining({ name: 'UpdateFunctionPropertiesToFinish', delay: 5, }), })); }); test('calls the updateLambdaConfiguration() API when it receives difference in Description field of a Lambda function', async () => { // GIVEN setup.setCurrentCfnStackTemplate({ Resources: { Func: { Type: 'AWS::Lambda::Function', Properties: { Code: { S3Bucket: 's3-bucket', S3Key: 's3-key', }, FunctionName: 'my-function', Description: 'Old Description', }, Metadata: { 'aws:asset:path': 'asset-path', }, }, }, }); const cdkStackArtifact = setup.cdkStackArtifactOf({ template: { Resources: { Func: { Type: 'AWS::Lambda::Function', Properties: { Code: { S3Bucket: 's3-bucket', S3Key: 's3-key', }, FunctionName: 'my-function', Description: 'New Description', }, Metadata: { 'aws:asset:path': 'asset-path', }, }, }, }, }); // WHEN const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact); // THEN expect(deployStackResult).not.toBeUndefined(); expect(mockUpdateLambdaConfiguration).toHaveBeenCalledWith({ FunctionName: 'my-function', Description: 'New Description', }); }); test('calls the updateLambdaConfiguration() API when it receives difference in Environment field of a Lambda function', async () => { // GIVEN setup.setCurrentCfnStackTemplate({ Resources: { Func: { Type: 'AWS::Lambda::Function', Properties: { Code: { S3Bucket: 's3-bucket', S3Key: 's3-key', }, FunctionName: 'my-function', Environment: { Variables: { Key1: 'Value1', Key2: 'Value2', }, }, }, Metadata: { 'aws:asset:path': 'asset-path', }, }, }, }); const cdkStackArtifact = setup.cdkStackArtifactOf({ template: { Resources: { Func: { Type: 'AWS::Lambda::Function', Properties: { Code: { S3Bucket: 's3-bucket', S3Key: 's3-key', }, FunctionName: 'my-function', Environment: { Variables: { Key1: 'Value1', Key2: 'Value2', NewKey: 'NewValue', }, }, }, Metadata: { 'aws:asset:path': 'asset-path', }, }, }, }, }); // WHEN const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact); // THEN expect(deployStackResult).not.toBeUndefined(); expect(mockUpdateLambdaConfiguration).toHaveBeenCalledWith({ FunctionName: 'my-function', Environment: { Variables: { Key1: 'Value1', Key2: 'Value2', NewKey: 'NewValue', }, }, }); }); test('calls both updateLambdaCode() and updateLambdaConfiguration() API when it receives both code and configuration change', async () => { // GIVEN setup.setCurrentCfnStackTemplate({ Resources: { Func: { Type: 'AWS::Lambda::Function', Properties: { Code: { S3Bucket: 'current-bucket', S3Key: 'current-key', }, FunctionName: 'my-function', Description: 'Old Description', }, Metadata: { 'aws:asset:path': 'asset-path', }, }, }, }); const cdkStackArtifact = setup.cdkStackArtifactOf({ template: { Resources: { Func: { Type: 'AWS::Lambda::Function', Properties: { Code: { S3Bucket: 'new-bucket', S3Key: 'new-key', }, FunctionName: 'my-function', Description: 'New Description', }, Metadata: { 'aws:asset:path': 'asset-path', }, }, }, }, }); // WHEN const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact); // THEN expect(deployStackResult).not.toBeUndefined(); expect(mockUpdateLambdaConfiguration).toHaveBeenCalledWith({ FunctionName: 'my-function', Description: 'New Description', }); expect(mockUpdateLambdaCode).toHaveBeenCalledWith({ FunctionName: 'my-function', S3Bucket: 'new-bucket', S3Key: 'new-key', }); }); test('Lambda hotswap works properly with changes of environment variables and description with tokens', async () => { // GIVEN setup.setCurrentCfnStackTemplate({ Resources: { EventBus: { Type: 'AWS::Events::EventBus', Properties: { Name: 'my-event-bus', }, }, Func: { Type: 'AWS::Lambda::Function', Properties: { Code: { S3Bucket: 's3-bucket', S3Key: 's3-key', }, FunctionName: 'my-function', Environment: { Variables: { token: { 'Fn::GetAtt': ['EventBus', 'Arn'] }, literal: 'oldValue', }, }, Description: { 'Fn::Join': ['', [ 'oldValue', { 'Fn::GetAtt': ['EventBus', 'Arn'] }, ]], }, }, Metadata: { 'aws:asset:path': 'asset-path', }, }, }, }); setup.pushStackResourceSummaries(setup.stackSummaryOf('EventBus', 'AWS::Events::EventBus', 'my-event-bus')); const cdkStackArtifact = setup.cdkStackArtifactOf({ template: { Resources: { EventBus: { Type: 'AWS::Events::EventBus', Properties: { Name: 'my-event-bus', }, }, Func: { Type: 'AWS::Lambda::Function', Properties: { Code: { S3Bucket: 's3-bucket', S3Key: 's3-key', }, FunctionName: 'my-function', Environment: { Variables: { token: { 'Fn::GetAtt': ['EventBus', 'Arn'] }, literal: 'newValue', }, }, Description: { 'Fn::Join': ['', [ 'newValue', { 'Fn::GetAtt': ['EventBus', 'Arn'] }, ]], }, }, Metadata: { 'aws:asset:path': 'asset-path', }, }, }, }, }); // WHEN const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact); // THEN expect(deployStackResult).not.toBeUndefined(); expect(mockUpdateLambdaConfiguration).toHaveBeenCalledWith({ FunctionName: 'my-function', Environment: { Variables: { token: 'arn:aws:events:here:123456789012:event-bus/my-event-bus', literal: 'newValue', }, }, Description: 'newValuearn:aws:events:here:123456789012:event-bus/my-event-bus', }); }); test('S3ObjectVersion is hotswappable', async () => { // GIVEN setup.setCurrentCfnStackTemplate({ Resources: { Func: { Type: 'AWS::Lambda::Function', Properties: { Code: { S3Key: 'current-key', S3ObjectVersion: 'current-obj', }, FunctionName: 'my-function', }, }, }, }); const cdkStackArtifact = setup.cdkStackArtifactOf({ template: { Resources: { Func: { Type: 'AWS::Lambda::Function', Properties: { Code: { S3Key: 'new-key', S3ObjectVersion: 'new-obj', }, FunctionName: 'my-function', }, }, }, }, }); // WHEN const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact); // THEN expect(deployStackResult).not.toBeUndefined(); expect(mockUpdateLambdaCode).toHaveBeenCalledWith({ FunctionName: 'my-function', S3Key: 'new-key', S3ObjectVersion: 'new-obj', }); }); }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGFtYmRhLWZ1bmN0aW9ucy1ob3Rzd2FwLWRlcGxveW1lbnRzLnRlc3QuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJsYW1iZGEtZnVuY3Rpb25zLWhvdHN3YXAtZGVwbG95bWVudHMudGVzdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUVBLDhDQUE4QztBQUM5Qyw0REFBOEQ7QUFFOUQsSUFBSSxvQkFBNEcsQ0FBQztBQUNqSCxJQUFJLDZCQUVtQyxDQUFDO0FBQ3hDLElBQUksZUFBbUYsQ0FBQztBQUN4RixJQUFJLHNCQUFvRCxDQUFDO0FBRXpELFVBQVUsQ0FBQyxHQUFHLEVBQUU7SUFDZCxzQkFBc0IsR0FBRyxLQUFLLENBQUMsaUJBQWlCLEVBQUUsQ0FBQztJQUNuRCxvQkFBb0IsR0FBRyxJQUFJLENBQUMsRUFBRSxFQUFFLENBQUMsZUFBZSxDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBQ3JELDZCQUE2QixHQUFHLElBQUksQ0FBQyxFQUFFLEVBQUUsQ0FBQyxlQUFlLENBQUMsRUFBRSxDQUFDLENBQUM7SUFDOUQsZUFBZSxHQUFHLElBQUksQ0FBQyxFQUFFLEVBQUUsQ0FBQyxlQUFlLENBQUM7UUFDMUMsT0FBTyxFQUFFLEdBQUcsRUFBRSxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDO1FBQ2xDLFFBQVEsRUFBRSxFQUFFO1FBQ1osWUFBWSxFQUFFLEdBQUcsRUFBRSxHQUFFLENBQUM7S0FDdkIsQ0FBQyxDQUFDO0lBQ0gsc0JBQXNCLENBQUMsVUFBVSxDQUFDO1FBQ2hDLGtCQUFrQixFQUFFLG9CQUFvQjtRQUN4QywyQkFBMkIsRUFBRSw2QkFBNkI7S0FDM0QsRUFBRTtRQUNELFdBQVcsRUFBRSxlQUFlO0tBQzdCLENBQUMsQ0FBQztBQUNMLENBQUMsQ0FBQyxDQUFDO0FBRUgsUUFBUSxDQUFDLElBQUksQ0FBQyxDQUFDLG9CQUFXLENBQUMsU0FBUyxFQUFFLG9CQUFXLENBQUMsWUFBWSxDQUFDLENBQUMsQ0FBQyxT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