aws-cdk
Version:
CDK Toolkit, the command line tool for CDK apps
664 lines • 80.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const setup = require("./hotswap-test-setup");
const common_1 = require("../../../lib/api/hotswap/common");
let mockUpdateMachineDefinition;
let hotswapMockSdkProvider;
beforeEach(() => {
hotswapMockSdkProvider = setup.setupHotswapTests();
mockUpdateMachineDefinition = jest.fn();
hotswapMockSdkProvider.setUpdateStateMachineMock(mockUpdateMachineDefinition);
});
describe.each([common_1.HotswapMode.FALL_BACK, common_1.HotswapMode.HOTSWAP_ONLY])('%p mode', (hotswapMode) => {
test('returns undefined when a new StateMachine is added to the Stack', async () => {
// GIVEN
const cdkStackArtifact = setup.cdkStackArtifactOf({
template: {
Resources: {
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
},
},
},
});
if (hotswapMode === common_1.HotswapMode.FALL_BACK) {
// WHEN
const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact);
// THEN
expect(deployStackResult).toBeUndefined();
expect(mockUpdateMachineDefinition).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(mockUpdateMachineDefinition).not.toHaveBeenCalled();
}
});
test('calls the updateStateMachine() API when it receives only a definitionString change without Fn::Join in a state machine', async () => {
// GIVEN
setup.setCurrentCfnStackTemplate({
Resources: {
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: '{ Prop: "old-value" }',
StateMachineName: 'my-machine',
},
},
},
});
const cdkStackArtifact = setup.cdkStackArtifactOf({
template: {
Resources: {
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: '{ Prop: "new-value" }',
StateMachineName: 'my-machine',
},
},
},
},
});
// WHEN
const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact);
// THEN
expect(deployStackResult).not.toBeUndefined();
expect(mockUpdateMachineDefinition).toHaveBeenCalledWith({
definition: '{ Prop: "new-value" }',
stateMachineArn: 'arn:aws:states:here:123456789012:stateMachine:my-machine',
});
});
test('calls the updateStateMachine() API when it receives only a definitionString change with Fn::Join in a state machine', async () => {
// GIVEN
setup.setCurrentCfnStackTemplate({
Resources: {
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: {
'Fn::Join': [
'\n',
[
'{',
' "StartAt" : "SuccessState"',
' "States" : {',
' "SuccessState": {',
' "Type": "Pass"',
' "Result": "Success"',
' "End": true',
' }',
' }',
'}',
],
],
},
StateMachineName: 'my-machine',
},
},
},
});
const cdkStackArtifact = setup.cdkStackArtifactOf({
template: {
Resources: {
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: {
'Fn::Join': [
'\n',
[
'{',
' "StartAt": "SuccessState",',
' "States": {',
' "SuccessState": {',
' "Type": "Succeed"',
' }',
' }',
'}',
],
],
},
StateMachineName: 'my-machine',
},
},
},
},
});
// WHEN
const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact);
// THEN
expect(deployStackResult).not.toBeUndefined();
expect(mockUpdateMachineDefinition).toHaveBeenCalledWith({
definition: JSON.stringify({
StartAt: 'SuccessState',
States: {
SuccessState: {
Type: 'Succeed',
},
},
}, null, 2),
stateMachineArn: 'arn:aws:states:here:123456789012:stateMachine:my-machine',
});
});
test('calls the updateStateMachine() API when it receives a change to the definitionString in a state machine that has no name', async () => {
// GIVEN
setup.setCurrentCfnStackTemplate({
Resources: {
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: '{ "Prop" : "old-value" }',
},
},
},
});
const cdkStackArtifact = setup.cdkStackArtifactOf({
template: {
Resources: {
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: '{ "Prop" : "new-value" }',
},
},
},
},
});
// WHEN
setup.pushStackResourceSummaries(setup.stackSummaryOf('Machine', 'AWS::StepFunctions::StateMachine', 'arn:aws:states:here:123456789012:stateMachine:my-machine'));
const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact);
// THEN
expect(deployStackResult).not.toBeUndefined();
expect(mockUpdateMachineDefinition).toHaveBeenCalledWith({
definition: '{ "Prop" : "new-value" }',
stateMachineArn: 'arn:aws:states:here:123456789012:stateMachine:my-machine',
});
});
test(`does not call the updateStateMachine() API when it receives a change to a property that is not the definitionString in a state machine
alongside a hotswappable change in CLASSIC mode but does in HOTSWAP_ONLY mode`, async () => {
// GIVEN
setup.setCurrentCfnStackTemplate({
Resources: {
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: '{ "Prop" : "old-value" }',
LoggingConfiguration: {
IncludeExecutionData: true,
},
},
},
},
});
const cdkStackArtifact = setup.cdkStackArtifactOf({
template: {
Resources: {
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: '{ "Prop" : "new-value" }',
LoggingConfiguration: {
IncludeExecutionData: false,
},
},
},
},
},
});
setup.pushStackResourceSummaries(setup.stackSummaryOf('Machine', 'AWS::StepFunctions::StateMachine', 'arn:aws:states:here:123456789012:stateMachine:my-machine'));
if (hotswapMode === common_1.HotswapMode.FALL_BACK) {
// WHEN
const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact);
// THEN
expect(deployStackResult).toBeUndefined();
expect(mockUpdateMachineDefinition).not.toHaveBeenCalled();
}
else if (hotswapMode === common_1.HotswapMode.HOTSWAP_ONLY) {
// WHEN
const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact);
// THEN
expect(deployStackResult).not.toBeUndefined();
expect(mockUpdateMachineDefinition).toHaveBeenCalledWith({
definition: '{ "Prop" : "new-value" }',
stateMachineArn: 'arn:aws:states:here:123456789012:stateMachine:my-machine',
});
}
});
test('does not call the updateStateMachine() API when a resource has a DefinitionString property but is not an AWS::StepFunctions::StateMachine is changed', async () => {
// GIVEN
setup.setCurrentCfnStackTemplate({
Resources: {
Machine: {
Type: 'AWS::NotStepFunctions::NotStateMachine',
Properties: {
DefinitionString: '{ Prop: "old-value" }',
},
},
},
});
const cdkStackArtifact = setup.cdkStackArtifactOf({
template: {
Resources: {
Machine: {
Type: 'AWS::NotStepFunctions::NotStateMachine',
Properties: {
DefinitionString: '{ Prop: "new-value" }',
},
},
},
},
});
if (hotswapMode === common_1.HotswapMode.FALL_BACK) {
// WHEN
const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact);
// THEN
expect(deployStackResult).toBeUndefined();
expect(mockUpdateMachineDefinition).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(mockUpdateMachineDefinition).not.toHaveBeenCalled();
}
});
test('can correctly hotswap old style synth changes', async () => {
// GIVEN
setup.setCurrentCfnStackTemplate({
Parameters: { AssetParam1: { Type: 'String' } },
Resources: {
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: { Ref: 'AssetParam1' },
StateMachineName: 'machine-name',
},
},
},
});
const cdkStackArtifact = setup.cdkStackArtifactOf({
template: {
Parameters: { AssetParam2: { Type: String } },
Resources: {
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: { Ref: 'AssetParam2' },
StateMachineName: 'machine-name',
},
},
},
},
});
// WHEN
setup.pushStackResourceSummaries(setup.stackSummaryOf('Machine', 'AWS::StepFunctions::StateMachine', 'arn:aws:states:here:123456789012:stateMachine:my-machine'));
const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact, { AssetParam2: 'asset-param-2' });
// THEN
expect(deployStackResult).not.toBeUndefined();
expect(mockUpdateMachineDefinition).toHaveBeenCalledWith({
definition: 'asset-param-2',
stateMachineArn: 'arn:aws:states:here:123456789012:stateMachine:machine-name',
});
});
test('calls the updateStateMachine() API when it receives a change to the definitionString that uses Attributes in a state machine', async () => {
// GIVEN
setup.setCurrentCfnStackTemplate({
Resources: {
Func: {
Type: 'AWS::Lambda::Function',
},
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: {
'Fn::Join': [
'\n',
[
'{',
' "StartAt" : "SuccessState"',
' "States" : {',
' "SuccessState": {',
' "Type": "Succeed"',
' }',
' }',
'}',
],
],
},
StateMachineName: 'my-machine',
},
},
},
});
const cdkStackArtifact = setup.cdkStackArtifactOf({
template: {
Resources: {
Func: {
Type: 'AWS::Lambda::Function',
},
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: {
'Fn::Join': [
'',
[
'"Resource": ',
{ 'Fn::GetAtt': ['Func', 'Arn'] },
],
],
},
StateMachineName: 'my-machine',
},
},
},
},
});
// WHEN
setup.pushStackResourceSummaries(setup.stackSummaryOf('Machine', 'AWS::StepFunctions::StateMachine', 'arn:aws:states:here:123456789012:stateMachine:my-machine'), setup.stackSummaryOf('Func', 'AWS::Lambda::Function', 'my-func'));
const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact);
// THEN
expect(deployStackResult).not.toBeUndefined();
expect(mockUpdateMachineDefinition).toHaveBeenCalledWith({
definition: '"Resource": arn:aws:lambda:here:123456789012:function:my-func',
stateMachineArn: 'arn:aws:states:here:123456789012:stateMachine:my-machine',
});
});
test("will not perform a hotswap deployment if it cannot find a Ref target (outside the state machine's name)", async () => {
// GIVEN
setup.setCurrentCfnStackTemplate({
Parameters: {
Param1: { Type: 'String' },
},
Resources: {
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: {
'Fn::Join': [
'',
[
'{ Prop: "old-value" }, ',
'{ "Param" : ',
{ 'Fn::Sub': '${Param1}' },
' }',
],
],
},
},
},
},
});
setup.pushStackResourceSummaries(setup.stackSummaryOf('Machine', 'AWS::StepFunctions::StateMachine', 'my-machine'));
const cdkStackArtifact = setup.cdkStackArtifactOf({
template: {
Parameters: {
Param1: { Type: 'String' },
},
Resources: {
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: {
'Fn::Join': [
'',
[
'{ Prop: "new-value" }, ',
'{ "Param" : ',
{ 'Fn::Sub': '${Param1}' },
' }',
],
],
},
},
},
},
},
});
// 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 state machines's name)", async () => {
// GIVEN
setup.setCurrentCfnStackTemplate({
Resources: {
Bucket: {
Type: 'AWS::S3::Bucket',
},
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: {
'Fn::Join': [
'',
[
'{ Prop: "old-value" }, ',
'{ "S3Bucket" : ',
{ 'Fn::GetAtt': ['Bucket', 'UnknownAttribute'] },
' }',
],
],
},
StateMachineName: 'my-machine',
},
},
},
});
setup.pushStackResourceSummaries(setup.stackSummaryOf('Machine', 'AWS::StepFunctions::StateMachine', 'arn:aws:states:here:123456789012:stateMachine:my-machine'), setup.stackSummaryOf('Bucket', 'AWS::S3::Bucket', 'my-bucket'));
const cdkStackArtifact = setup.cdkStackArtifactOf({
template: {
Resources: {
Bucket: {
Type: 'AWS::S3::Bucket',
},
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: {
'Fn::Join': [
'',
[
'{ Prop: "new-value" }, ',
'{ "S3Bucket" : ',
{ 'Fn::GetAtt': ['Bucket', 'UnknownAttribute'] },
' }',
],
],
},
StateMachineName: 'my-machine',
},
},
},
},
});
// 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('knows how to handle attributes of the AWS::Events::EventBus resource', async () => {
// GIVEN
setup.setCurrentCfnStackTemplate({
Resources: {
EventBus: {
Type: 'AWS::Events::EventBus',
Properties: {
Name: 'my-event-bus',
},
},
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: {
'Fn::Join': ['', [
'{"EventBus1Arn":"',
{ 'Fn::GetAtt': ['EventBus', 'Arn'] },
'","EventBus1Name":"',
{ 'Fn::GetAtt': ['EventBus', 'Name'] },
'","EventBus1Ref":"',
{ Ref: 'EventBus' },
'"}',
]],
},
StateMachineName: 'my-machine',
},
},
},
});
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',
},
},
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: {
'Fn::Join': ['', [
'{"EventBus2Arn":"',
{ 'Fn::GetAtt': ['EventBus', 'Arn'] },
'","EventBus2Name":"',
{ 'Fn::GetAtt': ['EventBus', 'Name'] },
'","EventBus2Ref":"',
{ Ref: 'EventBus' },
'"}',
]],
},
StateMachineName: 'my-machine',
},
},
},
},
});
// THEN
const result = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact);
expect(result).not.toBeUndefined();
expect(mockUpdateMachineDefinition).toHaveBeenCalledWith({
stateMachineArn: 'arn:aws:states:here:123456789012:stateMachine:my-machine',
definition: JSON.stringify({
EventBus2Arn: 'arn:aws:events:here:123456789012:event-bus/my-event-bus',
EventBus2Name: 'my-event-bus',
EventBus2Ref: 'my-event-bus',
}),
});
});
test('knows how to handle attributes of the AWS::DynamoDB::Table resource', async () => {
// GIVEN
setup.setCurrentCfnStackTemplate({
Resources: {
Table: {
Type: 'AWS::DynamoDB::Table',
Properties: {
KeySchema: [{
AttributeName: 'name',
KeyType: 'HASH',
}],
AttributeDefinitions: [{
AttributeName: 'name',
AttributeType: 'S',
}],
BillingMode: 'PAY_PER_REQUEST',
},
},
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: '{}',
StateMachineName: 'my-machine',
},
},
},
});
setup.pushStackResourceSummaries(setup.stackSummaryOf('Table', 'AWS::DynamoDB::Table', 'my-dynamodb-table'));
const cdkStackArtifact = setup.cdkStackArtifactOf({
template: {
Resources: {
Table: {
Type: 'AWS::DynamoDB::Table',
Properties: {
KeySchema: [{
AttributeName: 'name',
KeyType: 'HASH',
}],
AttributeDefinitions: [{
AttributeName: 'name',
AttributeType: 'S',
}],
BillingMode: 'PAY_PER_REQUEST',
},
},
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: {
'Fn::Join': ['', [
'{"TableName":"',
{ Ref: 'Table' },
'","TableArn":"',
{ 'Fn::GetAtt': ['Table', 'Arn'] },
'"}',
]],
},
StateMachineName: 'my-machine',
},
},
},
},
});
// THEN
const result = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact);
expect(result).not.toBeUndefined();
expect(mockUpdateMachineDefinition).toHaveBeenCalledWith({
stateMachineArn: 'arn:aws:states:here:123456789012:stateMachine:my-machine',
definition: JSON.stringify({
TableName: 'my-dynamodb-table',
TableArn: 'arn:aws:dynamodb:here:123456789012:table/my-dynamodb-table',
}),
});
});
test('does not explode if the DependsOn changes', async () => {
// GIVEN
setup.setCurrentCfnStackTemplate({
Resources: {
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: '{ Prop: "old-value" }',
StateMachineName: 'my-machine',
},
DependsOn: ['abc'],
},
},
});
const cdkStackArtifact = setup.cdkStackArtifactOf({
template: {
Resources: {
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: '{ Prop: "old-value" }',
StateMachineName: 'my-machine',
},
},
DependsOn: ['xyz'],
},
},
});
// WHEN
const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact);
// THEN
expect(deployStackResult).not.toBeUndefined();
expect(deployStackResult?.noOp).toEqual(true);
expect(mockUpdateMachineDefinition).not.toHaveBeenCalled();
});
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RhdGUtbWFjaGluZS1ob3Rzd2FwLWRlcGxveW1lbnRzLnRlc3QuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJzdGF0ZS1tYWNoaW5lLWhvdHN3YXAtZGVwbG95bWVudHMudGVzdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUVBLDhDQUE4QztBQUM5Qyw0REFBOEQ7QUFFOUQsSUFBSSwyQkFBa0ksQ0FBQztBQUN2SSxJQUFJLHNCQUFvRCxDQUFDO0FBRXpELFVBQVUsQ0FBQyxHQUFHLEVBQUU7SUFDZCxzQkFBc0IsR0FBRyxLQUFLLENBQUMsaUJBQWlCLEVBQUUsQ0FBQztJQUNuRCwyQkFBMkIsR0FBRyxJQUFJLENBQUMsRUFBRSxFQUFFLENBQUM7SUFDeEMsc0JBQXNCLENBQUMseUJBQXlCLENBQUMsMkJBQTJCLENBQUMsQ0FBQztBQUNoRixDQUFDLENBQUMsQ0FBQztBQUVILFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQyxvQkFBVyxDQUFDLFNBQVMsRUFBRSxvQkFBVyxDQUFDLFlBQVksQ0FBQyxDQUFDLENBQUMsU0FBUyxFQUFFLENBQUMsV0FBVyxFQUFFLEVBQUU7SUFDMUYsSUFBSSxDQUFDLGlFQUFpRSxFQUFFLEtBQUssSUFBSSxFQUFFO1FBQ2pGLFFBQVE7UUFDUixNQUFNLGdCQUFnQixHQUFHLEtBQUssQ0FBQyxrQkFBa0IsQ0FBQztZQUNoRCxRQUFRLEVBQUU7Z0JBQ1IsU0FBUyxFQUFFO29CQUNULE9BQU8sRUFBRTt3QkFDUCxJQUFJLEVBQUUsa0NBQWtDO3FCQUN6QztpQkFDRjthQUNGO1NBQ0YsQ0FBQyxDQUFDO1FBRUgsSUFBSSxXQUFXLEtBQUssb0JBQVcsQ0FBQyxTQUFTLEVBQUU7WUFDekMsT0FBTztZQUNQLE1BQU0saUJBQWlCLEdBQUcsTUFBTSxzQkFBc0IsQ0FBQyxvQkFBb0IsQ0FBQyxXQUFXLEVBQUUsZ0JBQWdCLENBQUMsQ0FBQztZQUUzRyxPQUFPO1lBQ1AsTUFBTSxDQUFDLGlCQUFpQixDQUFDLENBQUMs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RSxJQUFJLENBQUMsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDOUMsTUFBTSxDQUFDLDJCQUEyQixDQUFDLENBQUMsR0FBRyxDQUFDLGdCQUFnQixFQUFFLENBQUM7SUFDN0QsQ0FBQyxDQUFDLENBQUM7QUFDTCxDQUFDLENBQUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qIGVzbGludC1kaXNhYmxlIGltcG9ydC9vcmRlciAqL1xuaW1wb3J0IHsgU3RlcEZ1bmN0aW9ucyB9IGZyb20gJ2F3cy1zZGsnO1xuaW1wb3J0ICogYXMgc2V0dXAgZnJvbSAnLi9ob3Rzd2FwLXRlc3Qtc2V0dXAnO1xuaW1wb3J0IHsgSG90c3dhcE1vZGUgfSBmcm9tICcuLi8uLi8uLi9saWIvYXBpL2hvdHN3YXAvY29tbW9uJztcblxubGV0IG1vY2tVcGRhdGVNYWNoaW5lRGVmaW5pdGlvbjogKHBhcmFtczogU3RlcEZ1bmN0aW9ucy5UeXBlcy5VcGRhdGVTdGF0ZU1hY2hpbmVJbnB1dCkgPT4gU3RlcEZ1bmN0aW9ucy5UeXBlcy5VcGRhdGVTdGF0ZU1hY2hpbmVPdXRwdXQ7XG5sZXQgaG90c3dhcE1vY2tTZGtQcm92aWRlcjogc2V0dXAuSG90c3dhcE1vY2tTZGtQcm92aWRlcjtcblxuYmVmb3JlRWFjaCgoKSA9PiB7XG4gIGhvdHN3YXBNb2NrU2RrUHJvdmlkZXIgPSBzZXR1cC5zZXR1cEhvdHN3YXBUZXN0cygpO1xuICBtb2NrVXBkYXRlTWFjaGluZURlZmluaXRpb24gPSBqZXN0LmZuKCk7XG4gIGhvdHN3YXBNb2NrU2RrUHJvdmlkZXIuc2V0VXBkYXRlU3RhdGVNYWNoaW5lTW9jayhtb2NrVXBkYXRlTWFjaGluZURlZmluaXRpb24pO1xufSk7XG5cbmRlc2NyaWJlLmVhY2goW0hvdHN3YXBNb2RlLkZBTExfQkFDSywgSG90c3dhcE1vZGUuSE9UU1dBUF9PTkxZXSkoJyVwIG1vZGUnLCAoaG90c3dhcE1vZGUpID0+IHtcbiAgdGVzdCgncmV0dXJucyB1bmRlZmluZWQgd2hlbiBhIG5ldyBTdGF0ZU1hY2hpbmUgaXMgYWRkZWQgdG8gdGhlIFN0YWNrJywgYXN5bmMgKCkgPT4ge1xuICAgIC8vIEdJVkVOXG4gICAgY29uc3QgY2RrU3RhY2tBcnRpZmFjdCA9IHNldHVwLmNka1N0YWNrQXJ0aWZhY3RPZih7XG4gICAgICB0ZW1wbGF0ZToge1xuICAgICAgICBSZXNvdXJjZXM6IHtcbiAgICAgICAgICBNYWNoaW5lOiB7XG4gICAgICAgICAgICBUeXBlOiAnQVdTOjpTdGVwRnVuY3Rpb25zOjpTdGF0ZU1hY2hpbmUnLFxuICAgICAgICAgIH0sXG4gICAgICAgIH0sXG4gICAgICB9LFxuICAgIH0pO1xuXG4gICAgaWYgKGhvdHN3YXBNb2RlID09PSBIb3Rzd2FwTW9kZS5GQUxMX0JBQ0spIHtcbiAgICAgIC8vIFdIRU5cbiAgICAgIGNvbnN0IGRlcGxveVN0YWNrUmVzdWx0ID0gYX