UNPKG

@aws-solutions-constructs/core

Version:
471 lines 56.8 kB
"use strict"; /** * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance * with the License. A copy of the License is located at * * http://www.apache.org/licenses/LICENSE-2.0 * * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions * and limitations under the License. */ Object.defineProperty(exports, "__esModule", { value: true }); const cdk = require("aws-cdk-lib"); const assertions_1 = require("aws-cdk-lib/assertions"); const apigwv2 = require("aws-cdk-lib/aws-apigatewayv2"); const aws_apigatewayv2_authorizers_1 = require("aws-cdk-lib/aws-apigatewayv2-authorizers"); const aws_apigatewayv2_integrations_1 = require("aws-cdk-lib/aws-apigatewayv2-integrations"); const iam = require("aws-cdk-lib/aws-iam"); const sqs = require("aws-cdk-lib/aws-sqs"); const __1 = require(".."); const utils = require("../lib/utils"); test("creates API Gateway role and grants permissions and apigateway stage setup", () => { const app = new cdk.App(); const stack = new cdk.Stack(app, "TestStack"); const queue = new sqs.Queue(stack, "TestQueue", { fifo: true, deadLetterQueue: { queue: new sqs.Queue(stack, "DeadLetterQueue", { fifo: true, }), maxReceiveCount: 10, }, }); (0, __1.buildWebSocketQueueApi)(stack, "TestApi", { queue, createDefaultRoute: true, }); const template = assertions_1.Template.fromStack(stack); template.resourceCountIs("AWS::IAM::Role", 1); template.hasResourceProperties("AWS::IAM::Role", { AssumeRolePolicyDocument: { Statement: [ { Action: "sts:AssumeRole", Effect: "Allow", Principal: { Service: "apigateway.amazonaws.com", }, }, ], }, }); template.resourceCountIs("AWS::SQS::Queue", 2); template.hasResourceProperties("AWS::SQS::Queue", { FifoQueue: true, RedrivePolicy: { deadLetterTargetArn: { "Fn::GetAtt": [assertions_1.Match.anyValue(), "Arn"], }, maxReceiveCount: 10, }, }); template.hasResourceProperties("AWS::SQS::Queue", { FifoQueue: true, RedrivePolicy: assertions_1.Match.absent(), }); const sqsQueueCapture = new assertions_1.Capture(); template.hasResourceProperties("AWS::IAM::Policy", { PolicyDocument: { Statement: [ { Action: ["sqs:SendMessage", "sqs:GetQueueAttributes", "sqs:GetQueueUrl"], Effect: "Allow", Resource: { "Fn::GetAtt": [sqsQueueCapture, "Arn"], }, }, { Action: [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:DescribeLogGroups", "logs:DescribeLogStreams", "logs:PutLogEvents", "logs:GetLogEvents", "logs:FilterLogEvents", ], Effect: "Allow", Resource: { "Fn::GetAtt": [assertions_1.Match.stringLikeRegexp("LogGroup*"), "Arn"], }, }, ], }, }); template.resourceCountIs("AWS::ApiGatewayV2::Api", 1); template.resourceCountIs("AWS::ApiGatewayV2::Stage", 1); template.hasResourceProperties("AWS::ApiGatewayV2::Stage", { StageName: "prod", AutoDeploy: true, }); const apigwv2Catpure = new assertions_1.Capture(); template.resourceCountIs("AWS::ApiGatewayV2::Route", 2); template.hasResourceProperties("AWS::ApiGatewayV2::Route", { ApiId: { Ref: apigwv2Catpure, }, AuthorizationType: "NONE", RouteKey: "$default", Target: { "Fn::Join": [ "", [ "integrations/", { Ref: assertions_1.Match.stringLikeRegexp("WebSocketApiTestApidefaultRoutedefault"), }, ], ], }, }); template.hasResourceProperties("AWS::ApiGatewayV2::Route", { ApiId: { Ref: apigwv2Catpure, }, AuthorizationType: "AWS_IAM", RouteKey: "$connect", Target: { "Fn::Join": [ "", [ "integrations/", { Ref: assertions_1.Match.stringLikeRegexp("WebSocketApiTestApiconnectRouteconnect"), }, ], ], }, }); template.hasResourceProperties("AWS::ApiGatewayV2::Integration", { ApiId: { Ref: apigwv2Catpure.asString(), }, CredentialsArn: { "Fn::GetAtt": [assertions_1.Match.anyValue(), "Arn"], }, IntegrationMethod: "POST", IntegrationType: "AWS", IntegrationUri: { "Fn::Join": [ "", [ "arn:", { Ref: "AWS::Partition", }, ":apigateway:", { Ref: "AWS::Region", }, ":sqs:path/", { Ref: "AWS::AccountId", }, "/", { "Fn::GetAtt": [sqsQueueCapture.asString(), "QueueName"], }, ], ], }, PassthroughBehavior: "NEVER", RequestParameters: { "integration.request.header.Content-Type": "'application/x-www-form-urlencoded'", }, RequestTemplates: { $default: __1.DEFAULT_ROUTE_QUEUE_VTL_CONFIG, }, TemplateSelectionExpression: "\\$default", }); }); test("creates API Gateway role and grants permissions and apigateway stage setup", () => { const app = new cdk.App(); const stack = new cdk.Stack(app, "TestStack"); new apigwv2.WebSocketApi(stack, `WebSocketApi`, { apiName: "TestApi", connectRouteOptions: { integration: new aws_apigatewayv2_integrations_1.WebSocketMockIntegration("connect-mock"), authorizer: new aws_apigatewayv2_authorizers_1.WebSocketIamAuthorizer(), }, disconnectRouteOptions: { integration: new aws_apigatewayv2_integrations_1.WebSocketMockIntegration("disconnect-mock"), }, defaultRouteOptions: { integration: new aws_apigatewayv2_integrations_1.WebSocketMockIntegration("default-mock"), }, }); const template = assertions_1.Template.fromStack(stack); template.resourceCountIs("AWS::ApiGatewayV2::Api", 1); template.resourceCountIs("AWS::ApiGatewayV2::Stage", 0); template.resourceCountIs("AWS::ApiGatewayV2::Integration", 3); template.allResourcesProperties("AWS::ApiGatewayV2::Integration", { IntegrationType: "MOCK", }); template.hasResourceProperties("AWS::ApiGatewayV2::Route", { ApiId: assertions_1.Match.anyValue(), AuthorizationType: "AWS_IAM", RouteKey: "$connect", Target: { "Fn::Join": ["", ["integrations/", { Ref: assertions_1.Match.stringLikeRegexp("WebSocketApiconnectRouteconnectmock") }]], }, }); template.hasResourceProperties("AWS::ApiGatewayV2::Route", { ApiId: assertions_1.Match.anyValue(), RouteKey: "$disconnect", Target: { "Fn::Join": ["", ["integrations/", { Ref: assertions_1.Match.stringLikeRegexp("WebSocketApidisconnectRoutedisconnectmock") }]], }, }); template.hasResourceProperties("AWS::ApiGatewayV2::Route", { ApiId: assertions_1.Match.anyValue(), RouteKey: "$default", Target: { "Fn::Join": ["", ["integrations/", { Ref: assertions_1.Match.stringLikeRegexp("WebSocketApidefaultRoutedefaultmock") }]], }, }); }); test("buildWebSocketApiProps creates correct WebSocket API props", () => { const app = new cdk.App(); const stack = new cdk.Stack(app, "TestStack"); const role = new iam.Role(stack, "TestRole", { assumedBy: new iam.ServicePrincipal("apigateway.amazonaws.com"), }); const queue = new sqs.Queue(stack, "TestQueue"); const propsWithDefaultRoute = (0, __1.buildWebSocketApiProps)(role, queue, true); expect(propsWithDefaultRoute.defaultRouteOptions).toBeDefined(); expect(propsWithDefaultRoute.defaultRouteOptions).toEqual((0, __1.buildWebSocketQueueRouteOptions)(role, queue, "$default", { $default: __1.DEFAULT_ROUTE_QUEUE_VTL_CONFIG })); expect(propsWithDefaultRoute.connectRouteOptions).toEqual(__1.connectRouteOptions); const propsWithoutDefaultRoute = (0, __1.buildWebSocketApiProps)(role, queue, false); expect(propsWithoutDefaultRoute.defaultRouteOptions).toBeUndefined(); expect(() => (0, __1.buildWebSocketApiProps)(role, undefined, true)).toThrowError("role and sqs must be provided to create a default route"); expect(() => (0, __1.buildWebSocketApiProps)(undefined, queue, true)).toThrowError("role and sqs must be provided to create a default route"); }); test("buildWebSocketApiProps creates correct WebSocket API props with different values of defaultIamAuthorization and connectRouteOptions", () => { const app = new cdk.App(); const stack = new cdk.Stack(app, "TestStack"); const role = new iam.Role(stack, "TestRole", { assumedBy: new iam.ServicePrincipal("apigateway.amazonaws.com"), }); const queue = new sqs.Queue(stack, "TestQueue"); // do not create $default and $connect const apiPropsWithNoDefaultAndConnect = (0, __1.buildWebSocketApiProps)(role, queue, false, undefined, false); expect(apiPropsWithNoDefaultAndConnect.defaultRouteOptions).toBeUndefined(); expect(apiPropsWithNoDefaultAndConnect.connectRouteOptions).toBeUndefined(); // create $default, do not create $connect const apiPropsWithNoConnect = (0, __1.buildWebSocketApiProps)(role, queue, true, undefined, false); expect(apiPropsWithNoConnect.defaultRouteOptions).toBeDefined(); expect(apiPropsWithNoConnect.connectRouteOptions).toBeUndefined(); }); test("buildWebSocketQueueApi with defaultIamAuthorization is not set", () => { const app = new cdk.App(); const stack = new cdk.Stack(app, "TestStack"); const queue = new sqs.Queue(stack, "TestQueue"); // not supplying authorizer with $connect with the expectation that the synthesized stack with add // an authorizer because defaultIamAuthorization is not set to false. (0, __1.buildWebSocketQueueApi)(stack, "TestApi", { queue, createDefaultRoute: true, webSocketApiProps: { connectRouteOptions: { integration: new aws_apigatewayv2_integrations_1.WebSocketMockIntegration("user-created-mock"), }, }, }); const template = assertions_1.Template.fromStack(stack); template.hasResourceProperties("AWS::ApiGatewayV2::Route", { ApiId: assertions_1.Match.anyValue(), AuthorizationType: "AWS_IAM", RouteKey: "$connect", Target: { "Fn::Join": [ "", ["integrations/", { Ref: assertions_1.Match.stringLikeRegexp("WebSocketApiTestApiconnectRouteusercreatedmock") }], ], }, }); }); test("buildWebSocketQueueApi with defaultIamAuthorization is set to false and connectRouteOptions does not set authorizer", () => { const app = new cdk.App(); const stack = new cdk.Stack(app, "TestStack"); const queue = new sqs.Queue(stack, "TestQueue"); const printWarningSpy = jest.spyOn(utils, "printWarning"); // not supplying authorizer with $connect with the expectation that the synthesized stack with add // an authorizer because defaultIamAuthorization is not set to false. (0, __1.buildWebSocketQueueApi)(stack, "TestApi", { queue, createDefaultRoute: false, webSocketApiProps: { connectRouteOptions: { integration: new aws_apigatewayv2_integrations_1.WebSocketMockIntegration("user-created-mock"), }, }, defaultIamAuthorization: false, }); const template = assertions_1.Template.fromStack(stack); template.hasResourceProperties("AWS::ApiGatewayV2::Route", { ApiId: assertions_1.Match.anyValue(), AuthorizationType: "NONE", RouteKey: "$connect", Target: { "Fn::Join": [ "", ["integrations/", { Ref: assertions_1.Match.stringLikeRegexp("WebSocketApiTestApiconnectRouteusercreatedmock") }], ], }, }); expect(printWarningSpy).toBeCalledWith("This construct will create a WebSocket with NO Authorizer (defaultIamAuthorization is set to false)."); printWarningSpy.mockRestore(); }); test("buildWebSocketQueueApi when passing a custom route name, it should add a custom route integration to the websocket", () => { const app = new cdk.App(); const stack = new cdk.Stack(app, "TestStack"); const queue = new sqs.Queue(stack, "TestQueue"); const customRouteName = "fakeroutename"; (0, __1.buildWebSocketQueueApi)(stack, "TestApi", { queue, createDefaultRoute: false, defaultIamAuthorization: false, customRouteName, }); const template = assertions_1.Template.fromStack(stack); template.resourceCountIs("AWS::ApiGatewayV2::Integration", 1); template.hasResourceProperties("AWS::ApiGatewayV2::Integration", { ApiId: { Ref: assertions_1.Match.anyValue(), }, CredentialsArn: { "Fn::GetAtt": [assertions_1.Match.anyValue(), "Arn"], }, IntegrationMethod: "POST", IntegrationType: "AWS", IntegrationUri: { "Fn::Join": [ "", [ "arn:", { Ref: "AWS::Partition", }, ":apigateway:", { Ref: "AWS::Region", }, ":sqs:path/", { Ref: "AWS::AccountId", }, "/", { "Fn::GetAtt": [assertions_1.Match.anyValue(), "QueueName"], }, ], ], }, PassthroughBehavior: "NEVER", RequestParameters: { "integration.request.header.Content-Type": "'application/x-www-form-urlencoded'", }, RequestTemplates: { [customRouteName]: __1.DEFAULT_ROUTE_QUEUE_VTL_CONFIG, }, TemplateSelectionExpression: customRouteName, }); template.resourceCountIs("AWS::ApiGatewayV2::Route", 1); template.hasResourceProperties("AWS::ApiGatewayV2::Route", { ApiId: { Ref: assertions_1.Match.anyValue(), }, RouteKey: customRouteName, Target: { "Fn::Join": [ "", [ "integrations/", { Ref: assertions_1.Match.stringLikeRegexp("WebSocketApiTestApifakeroutenameRoutefakeroutename"), }, ], ], }, }); }); test("buildWebSocketQueueApi when passing a custom route name, with requestTemplate, should map the request template to the custom route", () => { const app = new cdk.App(); const stack = new cdk.Stack(app, "TestStack"); const queue = new sqs.Queue(stack, "TestQueue"); const customRouteName = "fakeroutename"; (0, __1.buildWebSocketQueueApi)(stack, "TestApi", { queue, createDefaultRoute: false, defaultIamAuthorization: false, customRouteName, defaultRouteRequestTemplate: { [customRouteName]: "a&fake&vtl", }, }); const template = assertions_1.Template.fromStack(stack); template.resourceCountIs("AWS::ApiGatewayV2::Integration", 1); template.hasResourceProperties("AWS::ApiGatewayV2::Integration", { ApiId: { Ref: assertions_1.Match.anyValue(), }, CredentialsArn: { "Fn::GetAtt": [assertions_1.Match.anyValue(), "Arn"], }, IntegrationMethod: "POST", IntegrationType: "AWS", IntegrationUri: { "Fn::Join": [ "", [ "arn:", { Ref: "AWS::Partition", }, ":apigateway:", { Ref: "AWS::Region", }, ":sqs:path/", { Ref: "AWS::AccountId", }, "/", { "Fn::GetAtt": [assertions_1.Match.anyValue(), "QueueName"], }, ], ], }, PassthroughBehavior: "NEVER", RequestParameters: { "integration.request.header.Content-Type": "'application/x-www-form-urlencoded'", }, RequestTemplates: { [customRouteName]: "a&fake&vtl", }, TemplateSelectionExpression: customRouteName, }); template.resourceCountIs("AWS::ApiGatewayV2::Route", 1); template.hasResourceProperties("AWS::ApiGatewayV2::Route", { ApiId: { Ref: assertions_1.Match.anyValue(), }, RouteKey: customRouteName, Target: { "Fn::Join": [ "", [ "integrations/", { Ref: assertions_1.Match.stringLikeRegexp("WebSocketApiTestApifakeroutenameRoutefakeroutename"), }, ], ], }, }); }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2Vic29ja2V0LWFwaS1oZWxwZXIudGVzdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIndlYnNvY2tldC1hcGktaGVscGVyLnRlc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Ozs7Ozs7OztHQVdHOztBQUVILG1DQUFtQztBQUNuQyx1REFBa0U7QUFDbEUsd0RBQXdEO0FBQ3hELDJGQUFrRjtBQUNsRiw2RkFBcUY7QUFDckYsMkNBQTJDO0FBQzNDLDJDQUEyQztBQUMzQywwQkFNWTtBQUNaLHNDQUFzQztBQUV0QyxJQUFJLENBQUMsNEVBQTRFLEVBQUUsR0FBRyxFQUFFO0lBQ3RGLE1BQU0sR0FBRyxHQUFHLElBQUksR0FBRyxDQUFDLEdBQUcsRUFBRSxDQUFDO0lBQzFCLE1BQU0sS0FBSyxHQUFHLElBQUksR0FBRyxDQUFDLEtBQUssQ0FBQyxHQUFHLEVBQUUsV0FBVyxDQUFDLENBQUM7SUFDOUMsTUFBTSxLQUFLLEdBQUcsSUFBSSxHQUFHLENBQUMsS0FBSyxDQUFDLEtBQUssRUFBRSxXQUFXLEVBQUU7UUFDOUMsSUFBSSxFQUFFLElBQUk7UUFDVixlQUFlLEVBQUU7WUFDZixLQUFLLEVBQUUsSUFBSSxHQUFHLENBQUMsS0FBSyxDQUFDLEtBQUssRUFBRSxpQkFBaUIsRUFBRTtnQkFDN0MsSUFBSSxFQUFFLElBQUk7YUFDWCxDQUFDO1lBQ0YsZUFBZSxFQUFFLEVBQUU7U0FDRztLQUN6QixDQUFDLENBQUM7SUFFSCxJQUFBLDBCQUFzQixFQUFDLEtBQUssRUFBRSxTQUFTLEVBQUU7UUFDdkMsS0FBSztRQUNMLGtCQUFrQixFQUFFLElBQUk7S0FDekIsQ0FBQyxDQUFDO0lBRUgsTUFBTSxRQUFRLEdBQUcscUJBQVEsQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUM7SUFFM0MsUUFBUSxDQUFDLGVBQWUsQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDLENBQUMsQ0FBQztJQUM5QyxRQUFRLENBQUMscUJBQXFCLENBQUMsZ0JBQWdC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QUNFLEdBQUcsRUFBRSxrQkFBSyxDQUFDLGdCQUFnQixDQUFDLG9EQUFvRCxDQUFDO3FCQUNsRjtpQkFDRjthQUNGO1NBQ0Y7S0FDRixDQUFDLENBQUM7QUFDTCxDQUFDLENBQUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogIENvcHlyaWdodCBBbWF6b24uY29tLCBJbmMuIG9yIGl0cyBhZmZpbGlhdGVzLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuICpcbiAqICBMaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpLiBZb3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlXG4gKiAgd2l0aCB0aGUgTGljZW5zZS4gQSBjb3B5IG9mIHRoZSBMaWNlbnNlIGlzIGxvY2F0ZWQgYXRcbiAqXG4gKiAgICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuICpcbiAqICBvciBpbiB0aGUgJ2xpY2Vuc2UnIGZpbGUgYWNjb21wYW55aW5nIHRoaXMgZmlsZS4gVGhpcyBmaWxlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuICdBUyBJUycgQkFTSVMsIFdJVEhPVVQgV0FSUkFOVElFU1xuICogIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGV4cHJlc3Mgb3IgaW1wbGllZC4gU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zXG4gKiAgYW5kIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuICovXG5cbmltcG9ydCAqIGFzIGNkayBmcm9tIFwiYXdzLWNkay1saWJcIjtcbmltcG9ydCB7IENhcHR1cmUsIE1hdGNoLCBUZW1wbGF0ZSB9IGZyb20gXCJhd3MtY2RrLWxpYi9hc3NlcnRpb25zXCI7XG5pbXBvcnQgKiBhcyBhcGlnd3YyIGZyb20gXCJhd3MtY2RrLWxpYi9hd3MtYXBpZ2F0ZXdheXYyXCI7XG5pbXBvcnQgeyBXZWJTb2NrZXRJYW1BdXRob3JpemVyIH0gZnJvbSBcImF3cy1jZGstbGliL2F3cy1hcGlnYXRld2F5djItYXV0aG9yaXplcnNcIjtcbmltcG9ydCB7IFdlYlNvY2tldE1vY2tJbnRlZ3JhdGlvbiB9IGZyb20gXCJhd3MtY2RrLWxpYi9hd3MtYXBpZ2F0ZXdheXYyLWludGVncmF0aW9uc1wiO1xuaW1wb3J0ICogYXMgaWFtIGZyb20gXCJhd3MtY2RrLWxpYi9hd3MtaWFtXCI7XG5pbXBvcnQgKiBhcyBzcXMgZnJvbSBcImF3cy1jZGstbGliL2F3cy1zcXNcIjtcbmltcG9ydCB7XG4gIGJ1aWxkV2ViU29ja2V0QXBpUHJvcHMsXG4gIGJ1aWxkV2ViU29ja2V0UXVldWVBcGksXG4gIGJ1aWxkV2ViU29ja2V0UXVldWVSb3V0ZU9wdGlvbnMsXG4gIGNvbm5lY3RSb3V0ZU9wdGlvbnMsXG4gIERFRkFVTFRfUk9VVEVfUVVFVUVfVlRMX0NPTkZJRyxcbn0gZnJvbSBcIi4uXCI7XG5pbXBvcnQgKiBhcyB1dGlscyBmcm9tIFwiLi4vbGliL3V0aWxzXCI7XG5cbnRlc3QoXCJjcmVhdGVzIEFQSSBHYXRld2F5IHJvbGUgYW5kIGdyYW50cyBwZXJtaXNzaW9ucyBhbmQgYXBpZ2F0ZXdheSBzdGFnZSBzZXR1cFwiLCAoKSA9PiB7XG4gIGNvbnN0IGFwcCA9IG5ldyBjZGsuQXBwKCk7XG4gIGNvbnN0IHN0YWNrID0gbmV3IGNkay5TdGFjayhhcHAsIFwiVGVzdFN0YWNrXCIpO1xuICBjb25zdCBxdWV1ZSA9IG5ldyBzcXMuUXVldWUoc3RhY2ssIFwiVGVzdFF1ZXVlXCIsIHtcbiAgICBmaWZvOiB0cnVlLFxuICAgIGRlYWRMZXR0ZXJRdWV1ZToge1xuICAgICAgcXVldWU6IG5ldyBzcXMuUXVldWUoc3RhY2ssIFwiRGVhZExldHRlclF1ZXVlXCIsIHtcbiAgICAgICAgZmlmbzogdHJ1ZSxcbiAgICAgIH0pLFxuICAgICAgbWF4UmVjZWl2ZUNvdW50OiAxMCxcbiAgICB9IGFzIHNxcy5EZWFkTGV0dGVyUXVldWUsXG4gIH0pO1xuXG4gIGJ1aWxkV2ViU29ja2V0UXVldWVBcGkoc3RhY2ssIFwiVGVzdEFwaVwiLCB7XG4gICAgcXVldWUsXG4gICAgY3JlYXRlRGVmYXVsdFJvdXRlOiB0cnVlLFxuICB9KTtcblxuICBjb25zdCB0ZW1wbGF0ZSA9IFRlbXBsYXRlLmZyb21TdGFjayhzdGFjayk7XG5cbiAgdGVtcGxhdGUucmVzb3VyY2VDb3VudElzKFwiQVdTOjpJQU06OlJvbGVcIiwgMSk7XG4gIHRlbXBsYXRlLmhhc1Jlc291cmNlUHJvcGVydGllcyhcIkFXUzo6SUFNOjpSb2xlXCIsIHtcbiAgICBBc3N1bWVSb2xlUG9saWN5RG9jdW1lbnQ6IHtcbiAgICAgIFN0YXRlbWVudDogW1xuICAgICAgICB7XG4gICAgICAgICAgQWN0aW9uOiBcInN0czpBc3N1bWVSb2xlXCIsXG4gICAgICAgICAgRWZmZWN0OiBcIkFsbG93XCIsXG4gICAgICAgICAgUHJpbmNpcGFsOiB7XG4gICAgICAgICAgICBTZXJ2aWNlOiBcImFwaWdhdGV3YXkuYW1hem9uYXdzLmNvbVwiLFxuICAgICAgICAgIH0sXG4gICAgICAgIH0sXG4gICAgICBdLFxuICAgIH0sXG4gIH0pO1xuXG4gIHRlbXBsYXRlLnJlc291cmNlQ291bnRJcyhcIkFXUzo6U1FTOjpRdWV1ZVwiLCAyKTtcbiAgdGVtcGxhdGUuaGFzUmVzb3VyY2VQcm9wZXJ0aWVzKFwiQVdTOjpTUVM6OlF1ZXVlXCIsIHtcbiAgICBGaWZvUXVldWU6IHRydWUsXG4gICAgUmVkcml2ZVBvbGljeToge1xuICAgICAgZGVhZExldHRlclRhcmdldEFybjoge1xuICAgICAgICBcIkZuOjpHZXRBdHRcIjogW01hdGNoLmFueVZhbHVlKCksIFwiQXJuXCJdLFxuICAgICAgfSxcbiAgICAgIG1heFJlY2VpdmVDb3VudDogMTAsXG4gICAgfSxcbiAgfSk7XG4gIHRlbXBsYXRlLmhhc1Jlc291cmNlUHJvcGVydGllcyhcIkFXUzo6U1FTOjpRdWV1ZVwiLCB7XG4gICAgRmlmb1F1ZXVlOiB0cnVlLFxuICAgIFJlZHJpdmVQb2xpY3k6IE1hdGNoLmFic2VudCgpLFxuICB9KTtcblxuICBjb25zdCBzcXNRdWV1ZUNhcHR1cmUgPSBuZXcgQ2FwdHVyZSgpO1xuICB0ZW1wbGF0ZS5oYXNSZXNvdXJjZVByb3BlcnRpZXMoXCJBV1M6OklBTTo6UG9saWN5XCIsIHtcbiAgICBQb2xpY3lEb2N1bWVudDoge1xuICAgICAgU3RhdGVtZW50OiBbXG4gICAgICAgIHtcbiAgICAgICAgICBBY3Rpb246IFtcInNxczpTZW5kTWVzc2FnZVwiLCBcInNxczpHZXRRdWV1ZUF0dHJpYnV0ZXNcIiwgXCJzcXM6R2V0UXVldWVVcmxcIl0sXG4gICAgICAgICAgRWZmZWN0OiBcIkFsbG93XCIsXG4gICAgICAgICAgUmVzb3VyY2U6IHtcbiAgICAgICAgICAgIFwiRm46OkdldEF0dFwiOiBbc3FzUXVldWVDYXB0dXJlLCBcIkFyblwiXSxcbiAgICAgICAgICB9LFxuICAgICAgICB9LFxuICAgICAgICB7XG4gICAgICAgICAgQWN0aW9uOiBbXG4gICAgICAgICAgICBcImxvZ3M6Q3JlYXRlTG9nR3JvdXBcIixcbiAgICAgICAgICAgIFwibG9nczpDcmVhdGVMb2dTdHJlYW1cIixcbiAgICAgICAgICAgIFwibG9nczpEZXNjcmliZUxvZ0dyb3Vwc1wiLFxuICAgICAgICAgICAgXCJsb2dzOkRlc2NyaWJlTG9nU3RyZWFtc1wiLFxuICAgICAgICAgICAgXCJsb2dzOlB1dExvZ0V2ZW50c1wiLFxuICAgICAgICAgICAgXCJsb2dzOkdldExvZ0V2ZW50c1wiLFxuICAgICAgICAgICAgXCJsb2dzOkZpbHRlckxvZ0V2ZW50c1wiLFxuICAgICAgICAgIF0sXG4gICAgICAgICAgRWZmZWN0OiBcIkFsbG93XCIsXG4gICAgICAgICAgUmVzb3VyY2U6IHtcbiAgICAgICAgICAgIFwiRm46OkdldEF0dFwiOiBbTWF0Y2guc3RyaW5nTGlrZVJlZ2V4cChcIkxvZ0dyb3VwKlwiKSwgXCJBcm5cIl0sXG4gICAgICAgICAgfSxcbiAgICAgICAgfSxcbiAgICAgIF0sXG4gICAgfSxcbiAgfSk7XG5cbiAgdGVtcGxhdGUucmVzb3VyY2VDb3VudElzKFwiQVdTOjpBcGlHYXRld2F5VjI6OkFwaVwiLCAxKTtcbiAgdGVtcGxhdGUucmVzb3VyY2VDb3VudElzKFwiQVdTOjpBcGlHYXRld2F5VjI6OlN0YWdlXCIsIDEpO1xuICB0ZW1wbGF0ZS5oYXNSZXNvdXJjZVByb3BlcnRpZXMoXCJBV1M6OkFwaUdhdGV3YXlWMjo6U3RhZ2VcIiwge1xuICAgIFN0YWdlTmFtZTogXCJwcm9kXCIsXG4gICAgQXV0b0RlcGxveTogdHJ1ZSxcbiAgfSk7XG5cbiAgY29uc3QgYXBpZ3d2MkNhdHB1cmUgPSBuZXcgQ2FwdHVyZSgpO1xuICB0ZW1wbGF0ZS5yZXNvdXJjZUNvdW50SXMoXCJBV1M6OkFwaUdhdGV3YXlWMjo6Um91dGVcIiwgMik7XG4gIHRlbXBsYXRlLmhhc1Jlc291cmNlUHJvcGVydGllcyhcIkFXUzo6QXBpR2F0ZXdheVYyOjpSb3V0ZVwiLCB7XG4gICAgQXBpSWQ6IHtcbiAgICAgIFJlZjogYXBpZ3d2MkNhdHB1cmUsXG4gICAgfSxcbiAgICBBdXRob3JpemF0aW9uVHlwZTogXCJOT05FXCIsXG4gICAgUm91dGVLZXk6IFwiJGRlZmF1bHRcIixcbiAgICBUYXJnZXQ6IHtcbiAgICAgIFwiRm46OkpvaW5cIjogW1xuICAgICAgICBcIlwiLFxuICAgICAgICBbXG4gICAgICAgICAgXCJpbnRlZ3JhdGlvbnMvXCIsXG4gICAgICAgICAge1xuICAgICAgICAgICAgUmVmOiBNYXRjaC5zdHJpbmdMaWtlUmVnZXhwKFwiV2ViU29ja2V0QXBpVGVzdEFwaWRlZmF1bHRSb3V0ZWRlZmF1bHRcIiksXG4gICAgICAgICAgfSxcbiAgICAgICAgXSxcbiAgICAgIF0sXG4gICAgfSxcbiAgfSk7XG5cbiAgdGVtcGxhdGUuaGFzUmVzb3VyY2VQcm9wZXJ0aWVzKFwiQVdTOjpBcGlHYXRld2F5VjI6OlJvdXRlXCIsIHtcbiAgICBBcGlJZDoge1xuICAgICAgUmVmOiBhcGlnd3YyQ2F0cHVyZSxcbiAgICB9LFxuICAgIEF1dGhvcml6YXRpb25UeXBlOiBcIkFXU19JQU1cIixcbiAgICBSb3V0ZUtleTogXCIkY29ubmVjdFwiLFxuICAgIFRhcmdldDoge1xuICAgICAgXCJGbjo6Sm9pblwiOiBbXG4gICAgICAgIFwiXCIsXG4gICAgICAgIFtcbiAgICAgICAgICBcImludGVncmF0aW9ucy9cIixcbiAgICAgICAgICB7XG4gICAgICAgICAgICBSZWY6IE1hdGNoLnN0cmluZ0xpa2VSZWdleHAoXCJXZWJTb2NrZXRBcGlUZXN0QXBpY29ubmVjdFJvdXRlY29ubmVjdFwiKSxcbiAgICAgICAgICB9LFxuICAgICAgICBdLFxuICAgICAgXSxcbiAgICB9LFxuICB9KTtcblxuICB0ZW1wbGF0ZS5oYXNSZXNvdXJjZVByb3BlcnRpZXMoXCJBV1M6OkFwaUdhdGV3YXlWMjo6SW50ZWdyYXRpb25cIiwge1xuICAgIEFwaUlkOiB7XG4gICAgICBSZWY6IGFwaWd3djJDYXRwdXJlLmFzU3RyaW5nKCksXG4gICAgfSxcbiAgICBDcmVkZW50aWFsc0Fybjoge1xuICAgICAgXCJGbjo6R2V0QXR0XCI6IFtNYXRjaC5hbnlWYWx1ZSgpLCBcIkFyblwiXSxcbiAgICB9LFxuICAgIEludGVncmF0aW9uTWV0aG9kOiBcIlBPU1RcIixcbiAgICBJbnRlZ3JhdGlvblR5cGU6IFwiQVdTXCIsXG4gICAgSW50ZWdyYXRpb25Vcmk6IHtcbiAgICAgIFwiRm46OkpvaW5cIjogW1xuICAgICAgICBcIlwiLFxuICAgICAgICBbXG4gICAgICAgICAgXCJhcm46XCIsXG4gICAgICAgICAge1xuICAgICAgICAgICAgUmVmOiBcIkFXUzo6UGFydGl0aW9uXCIsXG4gICAgICAgICAgfSxcbiAgICAgICAgICBcIjphcGlnYXRld2F5OlwiLFxuICAgICAgICAgIHtcbiAgICAgICAgICAgIFJlZjogXCJBV1M6OlJlZ2lvblwiLFxuICAgICAgICAgIH0sXG4gICAgICAgICAgXCI6c3FzOnBhdGgvXCIsXG4gICAgICAgICAge1xuICAgICAgICAgICAgUmVmOiBcIkFXUzo6QWNjb3VudElkXCIsXG4gICAgICAgICAgfSxcbiAgICAgICAgICBcIi9cIixcbiAgICAgICAgICB7XG4gICAgICAgICAgICBcIkZuOjpHZXRBdHRcIjogW3Nxc1F1ZXVlQ2FwdHVyZS5hc1N0cmluZygpLCBcIlF1ZXVlTmFtZVwiXSxcbiAgICAgICAgICB9LFxuICAgICAgICBdLFxuICAgICAgXSxcbiAgICB9LFxuICAgIFBhc3N0aHJvdWdoQmVoYXZpb3I6IFwiTkVWRVJcIixcbiAgICBSZXF1ZXN0UGFyYW1ldGVyczoge1xuICAgICAgXCJpbnRlZ3JhdGlvbi5yZXF1ZXN0LmhlYWRlci5Db250ZW50LVR5cGVcIjogXCInYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkJ1wiLFxuICAgIH0sXG4gICAgUmVxdWVzdFRlbXBsYXRlczoge1xuICAgICAgJGRlZmF1bHQ6IERFRkFVTFRfUk9VVEVfUVVFVUVfVlRMX0NPTkZJRyxcbiAgICB9LFxuICAgIFRlbXBsYXRlU2VsZWN0aW9uRXhwcmVzc2lvbjogXCJcXFxcJGRlZmF1bHRcIixcbiAgfSk7XG59KTtcblxudGVzdChcImNyZWF0ZXMgQVBJIEdhdGV3YXkgcm9sZSBhbmQgZ3JhbnRzIHBlcm1pc3Npb25zIGFuZCBhcGlnYXRld2F5IHN0YWdlIHNldHVwXCIsICgpID0+IHtcbiAgY29uc3QgYXBwID0gbmV3IGNkay5BcHAoKTtcbiAgY29uc3Qgc3RhY2sgPSBuZXcgY2RrLlN0YWNrKGFwcCwgXCJUZXN0U3RhY2tcIik7XG5cbiAgbmV3IGFwaWd3djIuV2ViU29ja2V0QXBpKHN0YWNrLCBgV2ViU29ja2V0QXBpYCwge1xuICAgIGFwaU5hbWU6IFwiVGVzdEFwaVwiLFxuICAgIGNvbm5lY3RSb3V0ZU9wdGlvbnM6IHtcbiAgICAgIGludGVncmF0aW9uOiBuZXcgV2ViU29ja2V0TW9ja0ludGVncmF0aW9uKFwiY29ubmVjdC1tb2NrXCIpLFxuICAgICAgYXV0aG9yaXplcjogbmV3IFdlYlNvY2tldElhbUF1dGhvcml6ZXIoKSxcbiAgICB9LFxuICAgIGRpc2Nvbm5lY3RSb3V0ZU9wdGlvbnM6IHtcbiAgICAgIGludGVncmF0aW9uOiBuZXcgV2ViU29ja2V0TW9ja0ludGVncmF0aW9uKFwiZGlzY29ubmVjdC1tb2NrXCIpLFxuICAgIH0sXG4gICAgZGVmYXVsdFJvdXRlT3B0aW9uczoge1xuICAgICAgaW50ZWdyYXRpb246IG5ldyBXZWJTb2NrZXRNb2NrSW50ZWdyYXRpb24oXCJkZWZhdWx0LW1vY2tcIiksXG4gICAgfSxcbiAgfSk7XG5cbiAgY29uc3QgdGVtcGxhdGUgPSBUZW1wbGF0ZS5mcm9tU3RhY2soc3RhY2spO1xuICB0ZW1wbGF0ZS5yZXNvdXJjZUNvdW50SXMoXCJBV1M6OkFwaUdhdGV3YXlWMjo6QXBpXCIsIDEpO1xuICB0ZW1wbGF0ZS5yZXNvdXJjZUNvdW50SXMoXCJBV1M6OkFwaUdhdGV3YXlWMjo6U3RhZ2VcIiwgMCk7XG4gIHRlbXBsYXRlLnJlc291cmNlQ291bnRJcyhcIkFXUzo6QXBpR2F0ZXdheVYyOjpJbnRlZ3JhdGlvblwiLCAzKTtcbiAgdGVtcGxhdGUuYWxsUmVzb3VyY2VzUHJvcGVydGllcyhcIkFXUzo6QXBpR2F0ZXdheVYyOjpJbnRlZ3JhdGlvblwiLCB7XG4gICAgSW50ZWdyYXRpb25UeXBlOiBcIk1PQ0tcIixcbiAgfSk7XG4gIHRlbXBsYXRlLmhhc1Jlc291cmNlUHJvcGVydGllcyhcIkFXUzo6QXBpR2F0ZXdheVYyOjpSb3V0ZVwiLCB7XG4gICAgQXBpSWQ6IE1hdGNoLmFueVZhbHVlKCksXG4gICAgQXV0aG9yaXphdGlvblR5cGU6IFwiQVdTX0lBTVwiLFxuICAgIFJvdXRlS2V5OiBcIiRjb25uZWN0XCIsXG4gICAgVGFyZ2V0OiB7XG4gICAgICBcIkZuOjpKb2luXCI6IFtcIlwiLCBbXCJpbnRlZ3JhdGlvbnMvXCIsIHsgUmVmOiBNYXRjaC5zdHJpbmdMaWtlUmVnZXhwKFwiV2ViU29ja2V0QXBpY29ubmVjdFJvdXRlY29ubmVjdG1vY2tcIikgfV1dLFxuICAgIH0sXG4gIH0pO1xuXG4gIHRlbXBsYXRlLmhhc1Jlc291cmNlUHJvcGVydGllcyhcIkFXUzo6QXBpR2F0ZXdheVYyOjpSb3V0ZVwiLCB7XG4gICAgQXBpSWQ6IE1hdGNoLmFueVZhbHVlKCksXG4gICAgUm91dGVLZXk6IFwiJGRpc2Nvbm5lY3RcIixcbiAgICBUYXJnZXQ6IHtcbiAgICAgIFwiRm46OkpvaW5cIjogW1wiXCIsIFtcImludGVncmF0aW9ucy9cIiwgeyBSZWY6IE1hdGNoLnN0cmluZ0xpa2VSZWdleHAoXCJXZWJTb2NrZXRBcGlkaXNjb25uZWN0Um91dGVkaXNjb25uZWN0bW9ja1wiKSB9XV0sXG4gICAgfSxcbiAgfSk7XG5cbiAgdGVtcGxhdGUuaGFzUmVzb3VyY2VQcm9wZXJ0aWVzKFwiQVdTOjpBcGlHYXRld2F5VjI6OlJvdXRlXCIsIHtcbiAgICBBcGlJZDogTWF0Y2guYW55VmFsdWUoKSxcbiAgICBSb3V0ZUtleTogXCIkZGVmYXVsdFwiLFxuICAgIFRhcmdldDoge1xuICAgICAgXCJGbjo6Sm9pblwiOiBbXCJcIiwgW1wiaW50ZWdyYXRpb25zL1wiLCB7IFJlZjogTWF0Y2guc3RyaW5nTGlrZVJlZ2V4cChcIldlYlNvY2tldEFwaWRlZmF1bHRSb3V0ZWRlZmF1bHRtb2NrXCIpIH1dXSxcbiAgICB9LFxuICB9KTtcbn0pO1xuXG50ZXN0KFwiYnVpbGRXZWJTb2NrZXRBcGlQcm9wcyBjcmVhdGVzIGNvcnJlY3QgV2ViU29ja2V0IEFQSSBwcm9wc1wiLCAoKSA9PiB7XG4gIGNvbnN0IGFwcCA9IG5ldyBjZGsuQXBwKCk7XG4gIGNvbnN0IHN0YWNrID0gbmV3IGNkay5TdGFjayhhcHAsIFwiVGVzdFN0YWNrXCIpO1xuICBjb25zdCByb2xlID0gbmV3IGlhbS5Sb2xlKHN0YWNrLCBcIlRlc3RSb2xlXCIsIHtcbiAgICBhc3N1bWVkQnk6IG5ldyBpYW0uU2VydmljZVByaW5jaXBhbChcImFwaWdhdGV3YXkuYW1hem9uYXdzLmNvbVwiKSxcbiAgfSk7XG4gIGNvbnN0IHF1ZXVlID0gbmV3IHNxcy5RdWV1ZShzdGFjaywgXCJUZXN0UXVldWVcIik7XG5cbiAgY29uc3QgcHJvcHNXaXRoRGVmYXVsdFJvdXRlID0gYnVpbGRXZWJTb2NrZXRBcGlQcm9wcyhyb2xlLCBxdWV1ZSwgdHJ1ZSk7XG4gIGV4cGVjdChwcm9wc1dpdGhEZWZhdWx0Um91dGUuZGVmYXVsdFJvdXRlT3B0aW9ucykudG9CZURlZmluZWQoKTtcbiAgZXhwZWN0KHByb3BzV2l0aERlZmF1bHRSb3V0ZS5kZWZhdWx0Um91dGVPcHRpb25zKS50b0VxdWFsKFxuICAgIGJ1aWxkV2ViU29ja2V0UXVldWVSb3V0ZU9wdGlvbnMocm9sZSwgcXVldWUsIFwiJGRlZmF1bHRcIiwgeyAkZGVmYXVsdDogREVGQVVMVF9ST1VURV9RVUVVRV9WVExfQ09ORklHIH0pXG4gICk7XG4gIGV4cGVjdChwcm9wc1dpdGhEZWZhdWx0Um91dGUuY29ubmVjdFJvdXRlT3B0aW9ucykudG9FcXVhbChjb25uZWN0Um91dGVPcHRpb25zKTtcblxuICBjb25zdCBwcm9wc1dpdGhvdXREZWZhdWx0Um91dGUgPSBidWlsZFdlYlNvY2tldEFwaVByb3BzKHJvbGUsIHF1ZXVlLCBmYWxzZSk7XG4gIGV4cGVjdChwcm9wc1dpdGhvdXREZWZhdWx0Um91dGUuZGVmYXVsdFJvdXRlT3B0aW9ucykudG9CZVVuZGVmaW5lZCgpO1xuXG4gIGV4cGVjdCgoKSA9PiBidWlsZFdlYlNvY2tldEFwaVByb3BzKHJvbGUsIHVuZGVmaW5lZCwgdHJ1ZSkpLnRvVGhyb3dFcnJvcihcbiAgICBcInJvbGUgYW5kIHNxcyBtdXN0IGJlIHByb3ZpZGVkIHRvIGNyZWF0ZSBhIGRlZmF1bHQgcm91dGVcIlxuICApO1xuICBleHBlY3QoKCkgPT4gYnVpbGRXZWJTb2NrZXRBcGlQcm9wcyh1bmRlZmluZWQsIHF1ZXVlLCB0cnVlKSkudG9UaHJvd0Vycm9yKFxuICAgIFwicm9sZSBhbmQgc3FzIG11c3QgYmUgcHJvdmlkZWQgdG8gY3JlYXRlIGEgZGVmYXVsdCByb3V0ZVwiXG4gICk7XG59KTtcblxudGVzdChcImJ1aWxkV2ViU29ja2V0QXBpUHJvcHMgY3JlYXRlcyBjb3JyZWN0IFdlYlNvY2tldCBBUEkgcHJvcHMgd2l0aCBkaWZmZXJlbnQgdmFsdWVzIG9mIGRlZmF1bHRJYW1BdXRob3JpemF0aW9uIGFuZCBjb25uZWN0Um91dGVPcHRpb25zXCIsICgpID0+IHtcbiAgY29uc3QgYXBwID0gbmV3IGNkay5BcHAoKTtcbiAgY29uc3Qgc3RhY2sgPSBuZXcgY2RrLlN0YWNrKGFwcCwgXCJUZXN0U3RhY2tcIik7XG4gIGNvbnN0IHJvbGUgPSBuZXcgaWFtLlJvbGUoc3RhY2ssIFwiVGVzdFJvbGVcIiwge1xuICAgIGFzc3VtZWRCeTogbmV3IGlhbS5TZXJ2aWNlUHJpbmNpcGFsKFwiYXBpZ2F0ZXdheS5hbWF6b25hd3MuY29tXCIpLFxuICB9KTtcbiAgY29uc3QgcXVldWUgPSBuZXcgc3FzLlF1ZXVlKHN0YWNrLCBcIlRlc3RRdWV1ZVwiKTtcblxuICAvLyBkbyBub3QgY3JlYXRlICRkZWZhdWx0IGFuZCAkY29ubmVjdFxuICBjb25zdCBhcGlQcm9wc1dpdGhOb0RlZmF1bHRBbmRDb25uZWN0ID0gYnVpbGRXZWJTb2NrZXRBcGlQcm9wcyhyb2xlLCBxdWV1ZSwgZmFsc2UsIHVuZGVmaW5lZCwgZmFsc2UpO1xuICBleHBlY3QoYXBpUHJvcHNXaXRoTm9EZWZhdWx0QW5kQ29ubmVjdC5kZWZhdWx0Um91dGVPcHRpb25zKS50b0JlVW5kZWZpbmVkKCk7XG4gIGV4cGVjdChhcGlQcm9wc1dpdGhOb0RlZmF1bHRBbmRDb25uZWN0LmNvbm5lY3RSb3V0ZU9wdGlvbnMpLnRvQmVVbmRlZmluZWQoKTtcblxuICAvLyBjcmVhdGUgJGRlZmF1bHQsIGRvIG5vdCBjcmVhdGUgJGNvbm5lY3RcbiAgY29uc3QgYXBpUHJvcHNXaXRoTm9Db25uZWN0ID0gYnVpbGRXZWJTb2NrZXRBcGlQcm9wcyhyb2xlLCBxdWV1ZSwgdHJ1ZSwgdW5kZWZpbmVkLCBmYWxzZSk7XG4gIGV4cGVjdChhcGlQcm9wc1dpdGhOb0Nvbm5lY3QuZGVmYXVsdFJvdXRlT3B0aW9ucykudG9CZURlZmluZWQoKTtcbiAgZXhwZWN0KGFwaVByb3BzV2l0aE5vQ29ubmVjdC5jb25uZWN0Um91dGVPcHRpb25zKS50b0JlVW5kZWZpbmVkKCk7XG59KTtcblxudGVzdChcImJ1aWxkV2ViU29ja2V0UXVldWVBcGkgd2l0aCBkZWZhdWx0SWFtQXV0aG9yaXphdGlvbiBpcyBub3Qgc2V0XCIsICgpID0+IHtcbiAgY29uc3QgYXBwID0gbmV3IGNkay5BcHAoKTtcbiAgY29uc3Qgc3RhY2sgPSBuZXcgY2RrLlN0YWNrKGFwcCwgXCJUZXN0U3RhY2tcIik7XG4gIGNvbnN0IHF1ZXVlID0gbmV3IHNxcy5RdWV1ZShzdGFjaywgXCJUZXN0UXVldWVcIik7XG5cbiAgLy8gbm90IHN1cHBseWluZyBhdXRob3JpemVyIHdpdGggJGNvbm5lY3Qgd2l0aCB0aGUgZXhwZWN0YXRpb24gdGhhdCB0aGUgc3ludGhlc2l6ZWQgc3RhY2sgd2l0aCBhZGRcbiAgLy8gYW4gYXV0aG9yaXplciBiZWNhdXNlIGRlZmF1bHRJYW1BdXRob3JpemF0aW9uIGlzIG5vdCBzZXQgdG8gZmFsc2UuXG4gIGJ1aWxkV2ViU29ja2V0UXVldWVBcGkoc3RhY2ssIFwiVGVzdEFwaVwiLCB7XG4gICAgcXVldWUsXG4gICAgY3JlYXRlRGVmYXVsdFJvdXRlOiB0cnVlLFxuICAgIHdlYlNvY2tldEFwaVByb3BzOiB7XG4gICAgICBjb25uZWN0Um91dGVPcHRpb25zOiB7XG4gICAgICAgIGludGVncmF0aW9uOiBuZXcgV2ViU29ja2V0TW9ja0ludGVncmF0aW9uKFwidXNlci1jcmVhdGVkLW1vY2tcIiksXG4gICAgICB9LFxuICAgIH0sXG4gIH0pO1xuXG4gIGNvbnN0IHRlbXBsYXRlID0gVGVtcGxhdGUuZnJvbVN0YWNrKHN0YWNrKTtcbiAgdGVtcGxhdGUuaGFzUmVzb3VyY2VQcm9wZXJ0aWVzKFwiQVdTOjpBcGlHYXRld2F5VjI6OlJvdXRlXCIsIHtcbiAgICBBcGlJZDogTWF0Y2guYW55VmFsdWUoKSxcbiAgICBBdXRob3JpemF0aW9uVHlwZTogXCJBV1NfSUFNXCIsXG4gICAgUm91dGVLZXk6IFwiJGNvbm5lY3RcIixcbiAgICBUYXJnZXQ6IHtcbiAgICAgIFwiRm46OkpvaW5cIjogW1xuICAgICAgICBcIlwiLFxuICAgICAgICBbXCJpbnRlZ3JhdGlvbnMvXCIsIHsgUmVmOiBNYXRjaC5zdHJpbmdMaWtlUmVnZXhwKFwiV2ViU29ja2V0QXBpVGVzdEFwaWNvbm5lY3RSb3V0ZXVzZXJjcmVhdGVkbW9ja1wiKSB9XSxcbiAgICAgIF0sXG4gICAgfSxcbiAgfSk7XG59KTtcblxudGVzdChcImJ1aWxkV2ViU29ja2V0UXVldWVBcGkgd2l0aCBkZWZhdWx0SWFtQXV0aG9yaXphdGlvbiBpcyBzZXQgdG8gZmFsc2UgYW5kIGNvbm5lY3RSb3V0ZU9wdGlvbnMgZG9lcyBub3Qgc2V0IGF1dGhvcml6ZXJcIiwgKCkgPT4ge1xuICBjb25zdCBhcHAgPSBuZXcgY2RrLkFwcCgpO1xuICBjb25zdCBzdGFjayA9IG5ldyBjZGsuU3RhY2soYXBwLCBcIlRlc3RTdGFja1wiKTtcbiAgY29uc3QgcXVldWUgPSBuZXcgc3FzLlF1ZXVlKHN0YWNrLCBcIlRlc3RRdWV1ZVwiKTtcbiAgY29uc3QgcHJpbnRXYXJuaW5nU3B5ID0gamVzdC5zcHlPbih1dGlscywgXCJwcmludFdhcm5pbmdcIik7XG5cbiAgLy8gbm90IHN1cHBseWluZyBhdXRob3JpemVyIHdpdGggJGNvbm5lY3Qgd2l0aCB0aGUgZXhwZWN0YXRpb24gdGhhdCB0aGUgc3ludGhlc2l6ZWQgc3RhY2sgd2l0aCBhZGRcbiAgLy8gYW4gYXV0aG9yaXplciBiZWNhdXNlIGRlZmF1bHRJYW1BdXRob3JpemF0aW9uIGlzIG5vdCBzZXQgdG8gZmFsc2UuXG4gIGJ1aWxkV2ViU29ja2V0UXVldWVBcGkoc3RhY2ssIFwiVGVzdEFwaVwiLCB7XG4gICAgcXVldWUsXG4gICAgY3JlYXRlRGVmYXVsdFJvdXRlOiBmYWxzZSxcbiAgICB3ZWJTb2NrZXRBcGlQcm9wczoge1xuICAgICAgY29ubmVjdFJvdXRlT3B0aW9uczoge1xuICAgICAgICBpbnRlZ3JhdGlvbjogbmV3IFdlYlNvY2