@cdklabs/cdk-appmod-catalog-blueprints 1.3.0 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.jsii +4 -4
- package/README.md +76 -98
- package/lib/document-processing/adapter/queued-s3-adapter.js +1 -1
- package/lib/document-processing/agentic-document-processing.js +1 -1
- package/lib/document-processing/base-document-processing.js +1 -1
- package/lib/document-processing/bedrock-document-processing.js +1 -1
- package/lib/document-processing/default-document-processing-config.js +1 -1
- package/lib/document-processing/tests/agentic-document-processing.test.js +104 -60
- package/lib/document-processing/tests/base-document-processing-nag.test.d.ts +1 -0
- package/lib/document-processing/tests/base-document-processing-nag.test.js +161 -0
- package/lib/document-processing/tests/base-document-processing.test.d.ts +1 -0
- package/lib/document-processing/tests/base-document-processing.test.js +499 -0
- package/lib/document-processing/tests/bedrock-document-processing.test.js +212 -36
- package/lib/document-processing/tests/queued-s3-adapter-nag.test.d.ts +1 -0
- package/lib/document-processing/tests/queued-s3-adapter-nag.test.js +122 -0
- package/lib/document-processing/tests/queued-s3-adapter.test.d.ts +1 -0
- package/lib/document-processing/tests/queued-s3-adapter.test.js +276 -0
- package/lib/framework/agents/base-agent.js +1 -1
- package/lib/framework/agents/batch-agent.js +1 -1
- package/lib/framework/agents/default-agent-config.js +1 -1
- package/lib/framework/bedrock/bedrock.js +1 -1
- package/lib/framework/custom-resource/default-runtimes.js +1 -1
- package/lib/framework/foundation/access-log.js +1 -1
- package/lib/framework/foundation/eventbridge-broker.js +1 -1
- package/lib/framework/foundation/network.js +1 -1
- package/lib/framework/tests/access-log.test.d.ts +1 -0
- package/lib/framework/tests/access-log.test.js +146 -0
- package/lib/framework/tests/batch-agent.test.d.ts +1 -0
- package/lib/framework/tests/batch-agent.test.js +164 -0
- package/lib/framework/tests/bedrock.test.d.ts +1 -0
- package/lib/framework/tests/bedrock.test.js +68 -0
- package/lib/framework/tests/eventbridge-broker.test.d.ts +1 -0
- package/lib/framework/tests/eventbridge-broker.test.js +73 -0
- package/lib/framework/tests/framework-nag.test.d.ts +1 -0
- package/lib/framework/tests/framework-nag.test.js +155 -0
- package/lib/framework/tests/network.test.d.ts +1 -0
- package/lib/framework/tests/network.test.js +120 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/utilities/data-loader.js +1 -1
- package/lib/utilities/lambda-iam-utils.js +1 -1
- package/lib/utilities/observability/cloudfront-distribution-observability-property-injector.js +1 -1
- package/lib/utilities/observability/default-observability-config.js +1 -1
- package/lib/utilities/observability/lambda-observability-property-injector.js +1 -1
- package/lib/utilities/observability/log-group-data-protection-utils.js +1 -1
- package/lib/utilities/observability/powertools-config.js +1 -1
- package/lib/utilities/observability/state-machine-observability-property-injector.js +1 -1
- package/lib/webapp/frontend-construct.js +1 -1
- package/package.json +8 -8
|
@@ -0,0 +1,499 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const aws_cdk_lib_1 = require("aws-cdk-lib");
|
|
4
|
+
const assertions_1 = require("aws-cdk-lib/assertions");
|
|
5
|
+
const aws_dynamodb_1 = require("aws-cdk-lib/aws-dynamodb");
|
|
6
|
+
const aws_kms_1 = require("aws-cdk-lib/aws-kms");
|
|
7
|
+
const aws_lambda_1 = require("aws-cdk-lib/aws-lambda");
|
|
8
|
+
const aws_s3_1 = require("aws-cdk-lib/aws-s3");
|
|
9
|
+
const aws_stepfunctions_tasks_1 = require("aws-cdk-lib/aws-stepfunctions-tasks");
|
|
10
|
+
const framework_1 = require("../../framework");
|
|
11
|
+
const eventbridge_broker_1 = require("../../framework/foundation/eventbridge-broker");
|
|
12
|
+
const adapter_1 = require("../adapter");
|
|
13
|
+
const base_document_processing_1 = require("../base-document-processing");
|
|
14
|
+
// Concrete test implementation of BaseDocumentProcessing
|
|
15
|
+
class TestDocumentProcessing extends base_document_processing_1.BaseDocumentProcessing {
|
|
16
|
+
constructor(scope, id, props) {
|
|
17
|
+
super(scope, id, props);
|
|
18
|
+
this.classificationFn = new aws_lambda_1.Function(this, 'ClassificationFn', {
|
|
19
|
+
runtime: aws_lambda_1.Runtime.NODEJS_20_X,
|
|
20
|
+
handler: 'index.handler',
|
|
21
|
+
code: aws_lambda_1.Code.fromInline('exports.handler = async () => ({ documentClassification: "TEST" });'),
|
|
22
|
+
});
|
|
23
|
+
this.processingFn = new aws_lambda_1.Function(this, 'ProcessingFn', {
|
|
24
|
+
runtime: aws_lambda_1.Runtime.NODEJS_20_X,
|
|
25
|
+
handler: 'index.handler',
|
|
26
|
+
code: aws_lambda_1.Code.fromInline('exports.handler = async () => ({ result: {} });'),
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
classificationStep() {
|
|
30
|
+
return new aws_stepfunctions_tasks_1.LambdaInvoke(this, 'MockClassification', {
|
|
31
|
+
lambdaFunction: this.classificationFn,
|
|
32
|
+
resultPath: '$.classificationResult',
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
processingStep() {
|
|
36
|
+
return new aws_stepfunctions_tasks_1.LambdaInvoke(this, 'MockProcessing', {
|
|
37
|
+
lambdaFunction: this.processingFn,
|
|
38
|
+
resultPath: '$.processingResult',
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
enrichmentStep() {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
postProcessingStep() {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
createStateMachine() {
|
|
48
|
+
return this.handleStateMachineCreation('test-state-machine');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// Test implementation with enrichment step
|
|
52
|
+
class TestDocumentProcessingWithEnrichment extends base_document_processing_1.BaseDocumentProcessing {
|
|
53
|
+
constructor(scope, id, props) {
|
|
54
|
+
super(scope, id, props);
|
|
55
|
+
this.classificationFn = new aws_lambda_1.Function(this, 'ClassificationFn', {
|
|
56
|
+
runtime: aws_lambda_1.Runtime.NODEJS_20_X,
|
|
57
|
+
handler: 'index.handler',
|
|
58
|
+
code: aws_lambda_1.Code.fromInline('exports.handler = async () => ({ documentClassification: "TEST" });'),
|
|
59
|
+
});
|
|
60
|
+
this.processingFn = new aws_lambda_1.Function(this, 'ProcessingFn', {
|
|
61
|
+
runtime: aws_lambda_1.Runtime.NODEJS_20_X,
|
|
62
|
+
handler: 'index.handler',
|
|
63
|
+
code: aws_lambda_1.Code.fromInline('exports.handler = async () => ({ result: {} });'),
|
|
64
|
+
});
|
|
65
|
+
this.enrichmentFn = new aws_lambda_1.Function(this, 'EnrichmentFn', {
|
|
66
|
+
runtime: aws_lambda_1.Runtime.NODEJS_20_X,
|
|
67
|
+
handler: 'index.handler',
|
|
68
|
+
code: aws_lambda_1.Code.fromInline('exports.handler = async () => ({ enriched: true });'),
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
classificationStep() {
|
|
72
|
+
return new aws_stepfunctions_tasks_1.LambdaInvoke(this, 'MockClassification', {
|
|
73
|
+
lambdaFunction: this.classificationFn,
|
|
74
|
+
resultPath: '$.classificationResult',
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
processingStep() {
|
|
78
|
+
return new aws_stepfunctions_tasks_1.LambdaInvoke(this, 'MockProcessing', {
|
|
79
|
+
lambdaFunction: this.processingFn,
|
|
80
|
+
resultPath: '$.processingResult',
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
enrichmentStep() {
|
|
84
|
+
return new aws_stepfunctions_tasks_1.LambdaInvoke(this, 'MockEnrichment', {
|
|
85
|
+
lambdaFunction: this.enrichmentFn,
|
|
86
|
+
resultPath: '$.enrichedResult',
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
postProcessingStep() {
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
createStateMachine() {
|
|
93
|
+
return this.handleStateMachineCreation('test-state-machine-enrichment');
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// Test implementation with post-processing step
|
|
97
|
+
class TestDocumentProcessingWithPostProcessing extends base_document_processing_1.BaseDocumentProcessing {
|
|
98
|
+
constructor(scope, id, props) {
|
|
99
|
+
super(scope, id, props);
|
|
100
|
+
this.classificationFn = new aws_lambda_1.Function(this, 'ClassificationFn', {
|
|
101
|
+
runtime: aws_lambda_1.Runtime.NODEJS_20_X,
|
|
102
|
+
handler: 'index.handler',
|
|
103
|
+
code: aws_lambda_1.Code.fromInline('exports.handler = async () => ({ documentClassification: "TEST" });'),
|
|
104
|
+
});
|
|
105
|
+
this.processingFn = new aws_lambda_1.Function(this, 'ProcessingFn', {
|
|
106
|
+
runtime: aws_lambda_1.Runtime.NODEJS_20_X,
|
|
107
|
+
handler: 'index.handler',
|
|
108
|
+
code: aws_lambda_1.Code.fromInline('exports.handler = async () => ({ result: {} });'),
|
|
109
|
+
});
|
|
110
|
+
this.postProcessingFn = new aws_lambda_1.Function(this, 'PostProcessingFn', {
|
|
111
|
+
runtime: aws_lambda_1.Runtime.NODEJS_20_X,
|
|
112
|
+
handler: 'index.handler',
|
|
113
|
+
code: aws_lambda_1.Code.fromInline('exports.handler = async () => ({ processed: true });'),
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
classificationStep() {
|
|
117
|
+
return new aws_stepfunctions_tasks_1.LambdaInvoke(this, 'MockClassification', {
|
|
118
|
+
lambdaFunction: this.classificationFn,
|
|
119
|
+
resultPath: '$.classificationResult',
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
processingStep() {
|
|
123
|
+
return new aws_stepfunctions_tasks_1.LambdaInvoke(this, 'MockProcessing', {
|
|
124
|
+
lambdaFunction: this.processingFn,
|
|
125
|
+
resultPath: '$.processingResult',
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
enrichmentStep() {
|
|
129
|
+
return undefined;
|
|
130
|
+
}
|
|
131
|
+
postProcessingStep() {
|
|
132
|
+
return new aws_stepfunctions_tasks_1.LambdaInvoke(this, 'MockPostProcessing', {
|
|
133
|
+
lambdaFunction: this.postProcessingFn,
|
|
134
|
+
resultPath: '$.postProcessedResult',
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
createStateMachine() {
|
|
138
|
+
return this.handleStateMachineCreation('test-state-machine-post-processing');
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
describe('BaseDocumentProcessing', () => {
|
|
142
|
+
let minimalStack;
|
|
143
|
+
let customStack;
|
|
144
|
+
let enrichmentStack;
|
|
145
|
+
let postProcessingStack;
|
|
146
|
+
let eventBridgeStack;
|
|
147
|
+
let minimalTemplate;
|
|
148
|
+
let customTemplate;
|
|
149
|
+
let enrichmentTemplate;
|
|
150
|
+
let postProcessingTemplate;
|
|
151
|
+
let eventBridgeTemplate;
|
|
152
|
+
beforeAll(() => {
|
|
153
|
+
// Minimal configuration
|
|
154
|
+
minimalStack = new aws_cdk_lib_1.Stack();
|
|
155
|
+
const minimalConstruct = new TestDocumentProcessing(minimalStack, 'MinimalTest', {});
|
|
156
|
+
minimalConstruct.createStateMachine();
|
|
157
|
+
// Custom configuration with custom table, bucket, and timeout
|
|
158
|
+
customStack = new aws_cdk_lib_1.Stack();
|
|
159
|
+
const customTable = new aws_dynamodb_1.Table(customStack, 'CustomTable', {
|
|
160
|
+
partitionKey: { name: 'DocumentId', type: aws_dynamodb_1.AttributeType.STRING },
|
|
161
|
+
});
|
|
162
|
+
const customKey = new aws_kms_1.Key(customStack, 'CustomKey', { enableKeyRotation: true });
|
|
163
|
+
const accessLog = new framework_1.AccessLog(customStack, 'AccessLog');
|
|
164
|
+
const customBucket = new aws_s3_1.Bucket(customStack, 'CustomBucket', {
|
|
165
|
+
serverAccessLogsBucket: accessLog.bucket,
|
|
166
|
+
serverAccessLogsPrefix: accessLog.bucketPrefix,
|
|
167
|
+
enforceSSL: true,
|
|
168
|
+
});
|
|
169
|
+
const customAdapter = new adapter_1.QueuedS3Adapter({ bucket: customBucket });
|
|
170
|
+
const customConstruct = new TestDocumentProcessing(customStack, 'CustomTest', {
|
|
171
|
+
documentProcessingTable: customTable,
|
|
172
|
+
ingressAdapter: customAdapter,
|
|
173
|
+
workflowTimeout: aws_cdk_lib_1.Duration.minutes(30),
|
|
174
|
+
removalPolicy: aws_cdk_lib_1.RemovalPolicy.RETAIN,
|
|
175
|
+
encryptionKey: customKey,
|
|
176
|
+
});
|
|
177
|
+
customConstruct.createStateMachine();
|
|
178
|
+
// With enrichment step
|
|
179
|
+
enrichmentStack = new aws_cdk_lib_1.Stack();
|
|
180
|
+
const enrichmentConstruct = new TestDocumentProcessingWithEnrichment(enrichmentStack, 'EnrichmentTest', {});
|
|
181
|
+
enrichmentConstruct.createStateMachine();
|
|
182
|
+
// With post-processing step
|
|
183
|
+
postProcessingStack = new aws_cdk_lib_1.Stack();
|
|
184
|
+
const postProcessingConstruct = new TestDocumentProcessingWithPostProcessing(postProcessingStack, 'PostProcessingTest', {});
|
|
185
|
+
postProcessingConstruct.createStateMachine();
|
|
186
|
+
// With EventBridge broker
|
|
187
|
+
eventBridgeStack = new aws_cdk_lib_1.Stack();
|
|
188
|
+
const broker = new eventbridge_broker_1.EventbridgeBroker(eventBridgeStack, 'TestBroker', {
|
|
189
|
+
name: 'test-broker',
|
|
190
|
+
eventSource: 'test-source',
|
|
191
|
+
});
|
|
192
|
+
const eventBridgeConstruct = new TestDocumentProcessing(eventBridgeStack, 'EventBridgeTest', {
|
|
193
|
+
eventbridgeBroker: broker,
|
|
194
|
+
});
|
|
195
|
+
eventBridgeConstruct.createStateMachine();
|
|
196
|
+
// Generate templates once
|
|
197
|
+
minimalTemplate = assertions_1.Template.fromStack(minimalStack);
|
|
198
|
+
customTemplate = assertions_1.Template.fromStack(customStack);
|
|
199
|
+
enrichmentTemplate = assertions_1.Template.fromStack(enrichmentStack);
|
|
200
|
+
postProcessingTemplate = assertions_1.Template.fromStack(postProcessingStack);
|
|
201
|
+
eventBridgeTemplate = assertions_1.Template.fromStack(eventBridgeStack);
|
|
202
|
+
});
|
|
203
|
+
describe('Basic functionality', () => {
|
|
204
|
+
test('creates construct with minimal configuration', () => {
|
|
205
|
+
expect(minimalTemplate).toBeDefined();
|
|
206
|
+
});
|
|
207
|
+
test('creates DynamoDB table with correct configuration', () => {
|
|
208
|
+
minimalTemplate.hasResourceProperties('AWS::DynamoDB::Table', {
|
|
209
|
+
KeySchema: [{
|
|
210
|
+
AttributeName: 'DocumentId',
|
|
211
|
+
KeyType: 'HASH',
|
|
212
|
+
}],
|
|
213
|
+
BillingMode: 'PAY_PER_REQUEST',
|
|
214
|
+
PointInTimeRecoverySpecification: {
|
|
215
|
+
PointInTimeRecoveryEnabled: true,
|
|
216
|
+
},
|
|
217
|
+
SSESpecification: {
|
|
218
|
+
SSEEnabled: true,
|
|
219
|
+
SSEType: 'KMS',
|
|
220
|
+
},
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
test('creates KMS encryption key with rotation enabled', () => {
|
|
224
|
+
minimalTemplate.hasResourceProperties('AWS::KMS::Key', {
|
|
225
|
+
EnableKeyRotation: true,
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
test('creates S3 bucket for document storage', () => {
|
|
229
|
+
minimalTemplate.hasResourceProperties('AWS::S3::Bucket', {
|
|
230
|
+
BucketEncryption: {
|
|
231
|
+
ServerSideEncryptionConfiguration: [{
|
|
232
|
+
ServerSideEncryptionByDefault: {
|
|
233
|
+
SSEAlgorithm: 'aws:kms',
|
|
234
|
+
},
|
|
235
|
+
}],
|
|
236
|
+
},
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
test('creates SQS queue with DLQ', () => {
|
|
240
|
+
minimalTemplate.hasResourceProperties('AWS::SQS::Queue', {
|
|
241
|
+
RedrivePolicy: assertions_1.Match.objectLike({
|
|
242
|
+
maxReceiveCount: 5,
|
|
243
|
+
}),
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
test('creates Step Functions state machine', () => {
|
|
247
|
+
minimalTemplate.resourceCountIs('AWS::StepFunctions::StateMachine', 1);
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
describe('State machine configuration', () => {
|
|
251
|
+
test('creates state machine with timeout', () => {
|
|
252
|
+
minimalTemplate.resourceCountIs('AWS::StepFunctions::StateMachine', 1);
|
|
253
|
+
});
|
|
254
|
+
test('creates state machine with custom timeout', () => {
|
|
255
|
+
customTemplate.resourceCountIs('AWS::StepFunctions::StateMachine', 1);
|
|
256
|
+
});
|
|
257
|
+
test('creates state machine with encryption', () => {
|
|
258
|
+
minimalTemplate.hasResourceProperties('AWS::StepFunctions::StateMachine', {
|
|
259
|
+
EncryptionConfiguration: {
|
|
260
|
+
Type: 'CUSTOMER_MANAGED_KMS_KEY',
|
|
261
|
+
},
|
|
262
|
+
});
|
|
263
|
+
});
|
|
264
|
+
test('creates state machine role with DynamoDB permissions', () => {
|
|
265
|
+
minimalTemplate.hasResourceProperties('AWS::IAM::Role', {
|
|
266
|
+
AssumeRolePolicyDocument: {
|
|
267
|
+
Statement: [{
|
|
268
|
+
Action: 'sts:AssumeRole',
|
|
269
|
+
Effect: 'Allow',
|
|
270
|
+
Principal: {
|
|
271
|
+
Service: 'states.amazonaws.com',
|
|
272
|
+
},
|
|
273
|
+
}],
|
|
274
|
+
},
|
|
275
|
+
});
|
|
276
|
+
minimalTemplate.hasResourceProperties('AWS::IAM::Policy', {
|
|
277
|
+
PolicyDocument: {
|
|
278
|
+
Statement: assertions_1.Match.arrayWith([
|
|
279
|
+
assertions_1.Match.objectLike({
|
|
280
|
+
Effect: 'Allow',
|
|
281
|
+
Resource: assertions_1.Match.anyValue(),
|
|
282
|
+
}),
|
|
283
|
+
]),
|
|
284
|
+
},
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
describe('Error handling chains', () => {
|
|
289
|
+
test('includes DynamoDB update for classification failure', () => {
|
|
290
|
+
minimalTemplate.hasResourceProperties('AWS::StepFunctions::StateMachine', {
|
|
291
|
+
DefinitionString: assertions_1.Match.objectLike({
|
|
292
|
+
'Fn::Join': assertions_1.Match.arrayWith(['']),
|
|
293
|
+
}),
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
test('includes DynamoDB update for processing failure', () => {
|
|
297
|
+
minimalTemplate.hasResourceProperties('AWS::StepFunctions::StateMachine', {
|
|
298
|
+
DefinitionString: assertions_1.Match.objectLike({
|
|
299
|
+
'Fn::Join': assertions_1.Match.arrayWith(['']),
|
|
300
|
+
}),
|
|
301
|
+
});
|
|
302
|
+
});
|
|
303
|
+
test('includes workflow status updates for success', () => {
|
|
304
|
+
minimalTemplate.hasResourceProperties('AWS::StepFunctions::StateMachine', {
|
|
305
|
+
DefinitionString: assertions_1.Match.objectLike({
|
|
306
|
+
'Fn::Join': assertions_1.Match.arrayWith(['']),
|
|
307
|
+
}),
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
});
|
|
311
|
+
describe('Optional workflow steps', () => {
|
|
312
|
+
test('includes enrichment step when provided', () => {
|
|
313
|
+
enrichmentTemplate.hasResourceProperties('AWS::StepFunctions::StateMachine', {
|
|
314
|
+
DefinitionString: assertions_1.Match.objectLike({
|
|
315
|
+
'Fn::Join': assertions_1.Match.arrayWith(['']),
|
|
316
|
+
}),
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
test('includes post-processing step when provided', () => {
|
|
320
|
+
postProcessingTemplate.hasResourceProperties('AWS::StepFunctions::StateMachine', {
|
|
321
|
+
DefinitionString: assertions_1.Match.objectLike({
|
|
322
|
+
'Fn::Join': assertions_1.Match.arrayWith(['']),
|
|
323
|
+
}),
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
test('marks workflow complete after processing when no optional steps', () => {
|
|
327
|
+
minimalTemplate.hasResourceProperties('AWS::StepFunctions::StateMachine', {
|
|
328
|
+
DefinitionString: assertions_1.Match.objectLike({
|
|
329
|
+
'Fn::Join': assertions_1.Match.arrayWith(['']),
|
|
330
|
+
}),
|
|
331
|
+
});
|
|
332
|
+
});
|
|
333
|
+
});
|
|
334
|
+
describe('Custom configuration', () => {
|
|
335
|
+
test('uses provided DynamoDB table', () => {
|
|
336
|
+
customTemplate.resourceCountIs('AWS::DynamoDB::Table', 1);
|
|
337
|
+
customTemplate.hasResourceProperties('AWS::DynamoDB::Table', {
|
|
338
|
+
KeySchema: [{
|
|
339
|
+
AttributeName: 'DocumentId',
|
|
340
|
+
KeyType: 'HASH',
|
|
341
|
+
}],
|
|
342
|
+
});
|
|
343
|
+
});
|
|
344
|
+
test('uses provided encryption key', () => {
|
|
345
|
+
customTemplate.resourceCountIs('AWS::KMS::Key', 2); // Custom key + adapter key
|
|
346
|
+
});
|
|
347
|
+
test('uses provided S3 bucket via adapter', () => {
|
|
348
|
+
customTemplate.hasResourceProperties('AWS::S3::Bucket', {
|
|
349
|
+
BucketEncryption: assertions_1.Match.objectLike({
|
|
350
|
+
ServerSideEncryptionConfiguration: assertions_1.Match.anyValue(),
|
|
351
|
+
}),
|
|
352
|
+
});
|
|
353
|
+
});
|
|
354
|
+
test('applies custom removal policy', () => {
|
|
355
|
+
customTemplate.hasResource('AWS::DynamoDB::Table', {
|
|
356
|
+
DeletionPolicy: 'Retain',
|
|
357
|
+
UpdateReplacePolicy: 'Retain',
|
|
358
|
+
});
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
describe('EventBridge integration', () => {
|
|
362
|
+
test('includes EventBridge event bus when broker provided', () => {
|
|
363
|
+
eventBridgeTemplate.hasResourceProperties('AWS::Events::EventBus', {
|
|
364
|
+
Name: 'test-broker',
|
|
365
|
+
});
|
|
366
|
+
});
|
|
367
|
+
test('includes EventBridge put events in state machine', () => {
|
|
368
|
+
eventBridgeTemplate.hasResourceProperties('AWS::StepFunctions::StateMachine', {
|
|
369
|
+
DefinitionString: assertions_1.Match.objectLike({
|
|
370
|
+
'Fn::Join': assertions_1.Match.arrayWith(['']),
|
|
371
|
+
}),
|
|
372
|
+
});
|
|
373
|
+
});
|
|
374
|
+
});
|
|
375
|
+
describe('Security', () => {
|
|
376
|
+
test('encrypts DynamoDB table with KMS', () => {
|
|
377
|
+
minimalTemplate.hasResourceProperties('AWS::DynamoDB::Table', {
|
|
378
|
+
SSESpecification: {
|
|
379
|
+
SSEEnabled: true,
|
|
380
|
+
SSEType: 'KMS',
|
|
381
|
+
},
|
|
382
|
+
});
|
|
383
|
+
});
|
|
384
|
+
test('encrypts S3 bucket with KMS', () => {
|
|
385
|
+
minimalTemplate.hasResourceProperties('AWS::S3::Bucket', {
|
|
386
|
+
BucketEncryption: {
|
|
387
|
+
ServerSideEncryptionConfiguration: [{
|
|
388
|
+
ServerSideEncryptionByDefault: {
|
|
389
|
+
SSEAlgorithm: 'aws:kms',
|
|
390
|
+
},
|
|
391
|
+
}],
|
|
392
|
+
},
|
|
393
|
+
});
|
|
394
|
+
});
|
|
395
|
+
test('encrypts SQS queue with KMS', () => {
|
|
396
|
+
minimalTemplate.hasResourceProperties('AWS::SQS::Queue', {
|
|
397
|
+
KmsMasterKeyId: assertions_1.Match.anyValue(),
|
|
398
|
+
});
|
|
399
|
+
});
|
|
400
|
+
test('encrypts state machine with customer managed key', () => {
|
|
401
|
+
minimalTemplate.hasResourceProperties('AWS::StepFunctions::StateMachine', {
|
|
402
|
+
EncryptionConfiguration: {
|
|
403
|
+
Type: 'CUSTOMER_MANAGED_KMS_KEY',
|
|
404
|
+
KmsKeyId: assertions_1.Match.anyValue(),
|
|
405
|
+
},
|
|
406
|
+
});
|
|
407
|
+
});
|
|
408
|
+
test('enforces SSL on S3 bucket', () => {
|
|
409
|
+
minimalTemplate.hasResourceProperties('AWS::S3::BucketPolicy', {
|
|
410
|
+
PolicyDocument: {
|
|
411
|
+
Statement: assertions_1.Match.arrayWith([
|
|
412
|
+
assertions_1.Match.objectLike({
|
|
413
|
+
Effect: 'Deny',
|
|
414
|
+
Condition: {
|
|
415
|
+
Bool: {
|
|
416
|
+
'aws:SecureTransport': 'false',
|
|
417
|
+
},
|
|
418
|
+
},
|
|
419
|
+
}),
|
|
420
|
+
]),
|
|
421
|
+
},
|
|
422
|
+
});
|
|
423
|
+
});
|
|
424
|
+
test('enforces SSL on SQS queue', () => {
|
|
425
|
+
minimalTemplate.hasResourceProperties('AWS::SQS::QueuePolicy', {
|
|
426
|
+
PolicyDocument: {
|
|
427
|
+
Statement: assertions_1.Match.arrayWith([
|
|
428
|
+
assertions_1.Match.objectLike({
|
|
429
|
+
Effect: 'Deny',
|
|
430
|
+
Condition: {
|
|
431
|
+
Bool: {
|
|
432
|
+
'aws:SecureTransport': 'false',
|
|
433
|
+
},
|
|
434
|
+
},
|
|
435
|
+
}),
|
|
436
|
+
]),
|
|
437
|
+
},
|
|
438
|
+
});
|
|
439
|
+
});
|
|
440
|
+
});
|
|
441
|
+
describe('Adapter integration', () => {
|
|
442
|
+
test('creates S3 event notification to SQS', () => {
|
|
443
|
+
minimalTemplate.hasResourceProperties('AWS::Lambda::Function', {
|
|
444
|
+
Handler: 'index.handler',
|
|
445
|
+
Environment: {
|
|
446
|
+
Variables: {
|
|
447
|
+
STATE_MACHINE_ARN: assertions_1.Match.anyValue(),
|
|
448
|
+
RAW_PREFIX: 'raw/',
|
|
449
|
+
},
|
|
450
|
+
},
|
|
451
|
+
});
|
|
452
|
+
});
|
|
453
|
+
test('creates SQS consumer Lambda with event source', () => {
|
|
454
|
+
minimalTemplate.hasResourceProperties('AWS::Lambda::EventSourceMapping', {
|
|
455
|
+
BatchSize: 10,
|
|
456
|
+
EventSourceArn: assertions_1.Match.anyValue(),
|
|
457
|
+
FunctionName: assertions_1.Match.anyValue(),
|
|
458
|
+
});
|
|
459
|
+
});
|
|
460
|
+
test('creates dead letter queue for failed messages', () => {
|
|
461
|
+
minimalTemplate.resourceCountIs('AWS::SQS::Queue', 2);
|
|
462
|
+
});
|
|
463
|
+
});
|
|
464
|
+
describe('Observability', () => {
|
|
465
|
+
test('creates construct without observability by default', () => {
|
|
466
|
+
const stack = new aws_cdk_lib_1.Stack();
|
|
467
|
+
const construct = new TestDocumentProcessing(stack, 'NoObservability', {
|
|
468
|
+
enableObservability: false,
|
|
469
|
+
});
|
|
470
|
+
construct.createStateMachine();
|
|
471
|
+
const template = assertions_1.Template.fromStack(stack);
|
|
472
|
+
template.hasResourceProperties('AWS::StepFunctions::StateMachine', {
|
|
473
|
+
LoggingConfiguration: assertions_1.Match.absent(),
|
|
474
|
+
});
|
|
475
|
+
});
|
|
476
|
+
test('enables observability when configured', () => {
|
|
477
|
+
const stack = new aws_cdk_lib_1.Stack();
|
|
478
|
+
const construct = new TestDocumentProcessing(stack, 'WithObservability', {
|
|
479
|
+
enableObservability: true,
|
|
480
|
+
});
|
|
481
|
+
construct.createStateMachine();
|
|
482
|
+
const template = assertions_1.Template.fromStack(stack);
|
|
483
|
+
template.hasResourceProperties('AWS::StepFunctions::StateMachine', {
|
|
484
|
+
LoggingConfiguration: assertions_1.Match.objectLike({
|
|
485
|
+
Level: 'ALL',
|
|
486
|
+
}),
|
|
487
|
+
});
|
|
488
|
+
});
|
|
489
|
+
});
|
|
490
|
+
describe('Resource counts', () => {
|
|
491
|
+
test('creates expected number of resources', () => {
|
|
492
|
+
minimalTemplate.resourceCountIs('AWS::DynamoDB::Table', 1);
|
|
493
|
+
minimalTemplate.resourceCountIs('AWS::S3::Bucket', 1);
|
|
494
|
+
minimalTemplate.resourceCountIs('AWS::SQS::Queue', 2);
|
|
495
|
+
minimalTemplate.resourceCountIs('AWS::StepFunctions::StateMachine', 1);
|
|
496
|
+
});
|
|
497
|
+
});
|
|
498
|
+
});
|
|
499
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmFzZS1kb2N1bWVudC1wcm9jZXNzaW5nLnRlc3QuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi91c2UtY2FzZXMvZG9jdW1lbnQtcHJvY2Vzc2luZy90ZXN0cy9iYXNlLWRvY3VtZW50LXByb2Nlc3NpbmcudGVzdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLDZDQUE2RDtBQUM3RCx1REFBeUQ7QUFDekQsMkRBQWdFO0FBQ2hFLGlEQUEwQztBQUMxQyx1REFBaUU7QUFDakUsK0NBQTRDO0FBQzVDLGlGQUFtRTtBQUNuRSwrQ0FBNEM7QUFDNUMsc0ZBQWtGO0FBQ2xGLHdDQUE2QztBQUM3QywwRUFBaUc7QUFFakcseURBQXlEO0FBQ3pELE1BQU0sc0JBQXVCLFNBQVEsaURBQXNCO0lBSXpELFlBQVksS0FBVSxFQUFFLEVBQVUsRUFBRSxLQUFVO1FBQzVDLEtBQUssQ0FBQyxLQUFLLEVBQUUsRUFBRSxFQUFFLEtBQUssQ0FBQyxDQUFDO1FBRXhCLElBQUksQ0FBQyxnQkFBZ0IsR0FBRyxJQUFJLHFCQUFRLENBQUMsSUFBSSxFQUFFLGtCQUFrQixFQUFFO1lBQzdELE9BQU8sRUFBRSxvQkFBTyxDQUFDLFdBQVc7WUFDNUIsT0FBTyxFQUFFLGVBQWU7WUFDeEIsSUFBSSxFQUFFLGlCQUFJLENBQUMsVUFBVSxDQUFDLHFFQUFxRSxDQUFDO1NBQzdGLENBQUMsQ0FBQztRQUVILElBQUksQ0FBQyxZQUFZLEdBQUcsSUFBSSxxQkFBUSxDQUFDLElBQUksRUFBRSxjQUFjLEVBQUU7WUFDckQsT0FBTyxFQUFFLG9CQUFPLENBQUMsV0FBVztZQUM1QixPQUFPLEVBQUUsZUFBZTtZQUN4QixJQUFJLEVBQUUsaUJBQUksQ0FBQyxVQUFVLENBQUMsaURBQWlELENBQUM7U0FDekUsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVTLGtCQUFrQjtRQUMxQixPQUFPLElBQUksc0NBQVksQ0FBQyxJQUFJLEVBQUUsb0JBQW9CLEVBQUU7WUFDbEQs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QUUsS0FBSztpQkFDYixDQUFDO2FBQ0gsQ0FBQyxDQUFDO1FBQ0wsQ0FBQyxDQUFDLENBQUM7SUFDTCxDQUFDLENBQUMsQ0FBQztJQUVILFFBQVEsQ0FBQyxpQkFBaUIsRUFBRSxHQUFHLEVBQUU7UUFDL0IsSUFBSSxDQUFDLHNDQUFzQyxFQUFFLEdBQUcsRUFBRTtZQUNoRCxlQUFlLENBQUMsZUFBZSxDQUFDLHNCQUFzQixFQUFFLENBQUMsQ0FBQyxDQUFDO1lBQzNELGVBQWUsQ0FBQyxlQUFlLENBQUMsaUJBQWlCLEVBQUUsQ0FBQyxDQUFDLENBQUM7WUFDdEQsZUFBZSxDQUFDLGVBQWUsQ0FBQyxpQkFBaUIsRUFBRSxDQUFDLENBQUMsQ0FBQztZQUN0RCxlQUFlLENBQUMsZUFBZSxDQUFDLGtDQUFrQyxFQUFFLENBQUMsQ0FBQyxDQUFDO1FBQ3pFLENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQyxDQUFDLENBQUM7QUFDTCxDQUFDLENBQUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IER1cmF0aW9uLCBSZW1vdmFsUG9saWN5LCBTdGFjayB9IGZyb20gJ2F3cy1jZGstbGliJztcbmltcG9ydCB7IFRlbXBsYXRlLCBNYXRjaCB9IGZyb20gJ2F3cy1jZGstbGliL2Fzc2VydGlvbnMnO1xuaW1wb3J0IHsgQXR0cmlidXRlVHlwZSwgVGFibGUgfSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtZHluYW1vZGInO1xuaW1wb3J0IHsgS2V5IH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWttcyc7XG5pbXBvcnQgeyBDb2RlLCBGdW5jdGlvbiwgUnVudGltZSB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1sYW1iZGEnO1xuaW1wb3J0IHsgQnVja2V0IH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLXMzJztcbmltcG9ydCB7IExhbWJkYUludm9rZSB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1zdGVwZnVuY3Rpb25zLXRhc2tzJztcbmltcG9ydCB7IEFjY2Vzc0xvZyB9IGZyb20gJy4uLy4uL2ZyYW1ld29yayc7XG5pbXBvcnQgeyBFdmVudGJyaWRnZUJyb2tlciB9IGZyb20gJy4uLy4uL2ZyYW1ld29yay9mb3VuZGF0aW9uL2V2ZW50YnJpZGdlLWJyb2tlcic7XG5pbXBvcnQgeyBRdWV1ZWRTM0FkYXB0ZXIgfSBmcm9tICcuLi9hZGFwdGVyJztcbmltcG9ydCB7IEJhc2VEb2N1bWVudFByb2Nlc3NpbmcsIERvY3VtZW50UHJvY2Vzc2luZ1N0ZXBUeXBlIH0gZnJvbSAnLi4vYmFzZS1kb2N1bWVudC1wcm9jZXNzaW5nJztcblxuLy8gQ29uY3JldGUgdGVzdCBpbXBsZW1lbnRhdGlvbiBvZiBCYXNlRG9jdW1lbnRQcm9jZXNzaW5nXG5jbGFzcyBUZXN0RG9jdW1lbnRQcm9jZXNzaW5nIGV4dGVuZHMgQmFzZURvY3VtZW50UHJvY2Vzc2luZyB7XG4gIHByaXZhdGUgY2xhc3NpZmljYXRpb25GbjogRnVuY3Rpb247XG4gIHByaXZhdGUgcHJvY2Vzc2luZ0ZuOiBGdW5jdGlvbjtcblxuICBjb25zdHJ1Y3RvcihzY29wZTogYW55LCBpZDogc3RyaW5nLCBwcm9wczogYW55KSB7XG4gICAgc3VwZXIoc2NvcGUsIGlkLCBwcm9wcyk7XG5cbiAgICB0aGlzLmNsYXNzaWZpY2F0aW9uRm4gPSBuZXcgRnVuY3Rpb24odGhpcywgJ0NsYXNzaWZpY2F0aW9uRm4nLCB7XG4gICAgICBydW50aW1lOiBSdW50aW1lLk5PREVKU18yMF9YLFxuICAgICAgaGFuZGxlcjogJ2luZGV4LmhhbmRsZXInLFxuICAgICAgY29kZTogQ29kZS5mcm9tSW5saW5lKCdleHBvcnRzLmhhbmRsZXIgPSBhc3luYyAoKSA9PiAoeyBkb2N1bWVudENsYXNzaWZpY2F0aW9uOiBcIlRFU1RcIiB9KTsnKSxcbiAgICB9KTtcblxuICAgIHRoaXMucHJvY2Vzc2luZ0ZuID0gbmV3IEZ1bmN0aW9uKHRoaXMsICdQcm9jZXNzaW5nRm4nLCB7XG4gICAgICBydW50aW1lOiBSdW50aW1lLk5PREVKU18yMF9YLFxuICAgICAgaGFuZGxlcjogJ2luZGV4LmhhbmRsZXInLFxuICAgICAgY29kZTogQ29kZS5mcm9tSW5saW5lKCdleHBvcnRzLmhhbmRsZXIgPSBhc3luYyAoKSA9PiAoeyByZXN1bHQ6IHt9IH0pOycpLFxuICAgIH0pO1xuICB9XG5cbiAgcHJvdGVjdGVkIGNsYXNzaWZpY2F0aW9uU3RlcCgpOiBEb2N1bWVudFByb2Nlc3NpbmdTdGVwVHlwZSB7XG4gICAgcmV0dXJuIG5ldyBMYW1iZGFJbnZva2UodGhpcywgJ01vY2tDbGFzc2lmaWNhdGlvbicsIHtcbiAgICAgIGxhbWJkYUZ1bmN0aW9uOiB0aGlzLmNsYXNzaWZpY2F0aW9uRm4sXG4gICAgICByZXN1bHRQYXRoOiAnJC5jbGFzc2lmaWNhdGlvblJlc3VsdCcsXG4gICAgfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgcHJvY2Vzc2luZ1N0ZXAoKTogRG9jdW1lbnRQcm9jZXNzaW5nU3RlcFR5cGUge1xuICAgIHJldHVybiBuZXcgTGFtYmRhSW52b2tlKHRoaXMsICdNb2NrUHJvY2Vzc2luZycsIHtcbiAgICAgIGxhbWJkYUZ1bmN0aW9uOiB0aGlzLnByb2Nlc3NpbmdGbixcbiAgICAgIHJlc3VsdFBhdGg6ICckLnByb2Nlc3NpbmdSZXN1bHQnLFxuICAgIH0pO1xuICB9XG5cbiAgcHJvdGVjdGVkIGVucmljaG1lbnRTdGVwKCk6IERvY3VtZW50UHJvY2Vzc2luZ1N0ZXBUeXBlIHwgdW5kZWZpbmVkIHtcbiAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9XG5cbiAgcHJvdGVjdGVkIHBvc3RQcm9jZXNzaW5nU3RlcCgpOiBEb2N1bWVudFByb2Nlc3NpbmdTdGVwVHlwZSB8IHVuZGVmaW5lZCB7XG4gICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgfVxuXG4gIHB1YmxpYyBjcmVhdGVTdGF0ZU1hY2hpbmUoKSB7XG4gICAgcmV0dXJuIHRoaXMuaGFuZGxlU3RhdGVNYWNoaW5lQ3JlYXRpb24oJ3Rlc3Qtc3RhdGUtbWFjaGluZScpO1xuICB9XG59XG5cbi8vIFRlc3QgaW1wbGVtZW50YXRpb24gd2l0aCBlbnJpY2htZW50IHN0ZXBcbmNsYXNzIFRlc3REb2N1bWVudFByb2Nlc3NpbmdXaXRoRW5yaWNobWVudCBleHRlbmRzIEJhc2VEb2N1bWVudFByb2Nlc3Npbmcge1xuICBwcml2YXRlIGNsYXNzaWZpY2F0aW9uRm46IEZ1bmN0aW9uO1xuICBwcml2YXRlIHByb2Nlc3NpbmdGbjogRnVuY3Rpb247XG4gIHByaXZhdGUgZW5yaWNobWVudEZuOiBGdW5jdGlvbjtcblxuICBjb25zdHJ1Y3RvcihzY29wZTogYW55LCBpZDogc3RyaW5nLCBwcm9wczogYW55KSB7XG4gICAgc3VwZXIoc2NvcGUsIGlkLCBwcm9wcyk7XG5cbiAgICB0aGlzLmNsYXNzaWZpY2F0aW9uRm4gPSBuZXcgRnVuY3Rpb24odGhpcywgJ0NsYXNzaWZpY2F0aW9uRm4nLCB7XG4gICAgICBydW50aW1lOiBSdW50aW1lLk5PREVKU18yMF9YLFxuICAgICAgaGFuZGxlcjogJ2luZGV4LmhhbmRsZXInLFxuICAgICAgY29kZTogQ29kZS5mcm9tSW5saW5lKCdleHBvcnRzLmhhbmRsZXIgPSBhc3luYyAoKSA9PiAoeyBkb2N1bWVudENsYXNzaWZpY2F0aW9uOiBcIlRFU1RcIiB9KTsnKSxcbiAgICB9KTtcblxuICAgIHRoaXMucHJvY2Vzc2luZ0ZuID0gbmV3IEZ1bmN0aW9uKHRoaXMsICdQcm9jZXNzaW5nRm4nLCB7XG4gICAgICBydW50aW1lOiBSdW50aW1lLk5PREVKU18yMF9YLFxuICAgICAgaGFuZGxlcjogJ2luZGV4LmhhbmRsZXInLFxuICAgICAgY29kZTogQ29kZS5mcm9tSW5saW5lKCdleHBvcnRzLmhhbmRsZXIgPSBhc3luYyAoKSA9PiAoeyByZXN1bHQ6IHt9IH0pOycpLFxuICAgIH0pO1xuXG4gICAgdGhpcy5lbnJpY2htZW50Rm4gPSBuZXcgRnVuY3Rpb24odGhpcywgJ0VucmljaG1lbnRGbicsIHtcbiAgICAgIHJ1bnRpbWU6IFJ1bnRpbWUuTk9ERUpTXzIwX1gsXG4gICAgICBoYW5kbGVyOiAnaW5kZXguaGFuZGxlcicsXG4gICAgICBjb2RlOiBDb2RlLmZyb21JbmxpbmUoJ2V4cG9ydHMuaGFuZGxlciA9IGFzeW5jICgpID0+ICh7IGVucmljaGVkOiB0cnVlIH0pOycpLFxuICAgIH0pO1xuICB9XG5cbiAgcHJvdGVjdGVkIGNsYXNzaWZpY2F0aW9uU3RlcCgpOiBEb2N1bWVudFByb2Nlc3NpbmdTdGVwVHlwZSB7XG4gICAgcmV0dXJuIG5ldyBMYW1iZGFJbnZva2UodGhpcywgJ01vY2tDbGFzc2lmaWNhdGlvbicsIHtcbiAgICAgIGxhbWJkYUZ1bmN0aW9uOiB0aGlzLmNsYXNzaWZpY2F0aW9uRm4sXG4gICAgICByZXN1bHRQYXRoOiAnJC5jbGFzc2lmaWNhdGlvblJlc3VsdCcsXG4gICAgfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgcHJvY2Vzc2luZ1N0ZXAoKTogRG9jdW1lbnRQcm9jZXNzaW5nU3RlcFR5cGUge1xuICAgIHJldHVybiBuZXcgTGFtYmRhSW52b2tlKHRoaXMsICdNb2NrUHJvY2Vzc2luZycsIHtcbiAgICAgIGxhbWJkYUZ1bmN0aW9uOiB0aGlzLnByb2Nlc3NpbmdGbixcbiAgICAgIHJlc3VsdFBhdGg6ICckLnByb2Nlc3NpbmdSZXN1bHQnLFxuICAgIH0pO1xuICB9XG5cbiAgcHJvdGVjdGVkIGVucmljaG1lbnRTdGVwKCk6IERvY3VtZW50UHJvY2Vzc2luZ1N0ZXBUeXBlIHwgdW5kZWZpbmVkIHtcbiAgICByZXR1cm4gbmV3IExhbWJkYUludm9rZSh0aGlzLCAnTW9ja0VucmljaG1lbnQnLCB7XG4gICAgICBsYW1iZGFGdW5jdGlvbjogdGhpcy5lbnJpY2htZW50Rm4sXG4gICAgICByZXN1bHRQYXRoOiAnJC5lbnJpY2hlZFJlc3VsdCcsXG4gICAgfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgcG9zdFByb2Nlc3NpbmdTdGVwKCk6IERvY3VtZW50UHJvY2Vzc2luZ1N0ZXBUeXBlIHwgdW5kZWZpbmVkIHtcbiAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9XG5cbiAgcHVibGljIGNyZWF0ZVN0YXRlTWFjaGluZSgpIHtcbiAgICByZXR1cm4gdGhpcy5oYW5kbGVTdGF0ZU1hY2hpbmVDcmVhdGlvbigndGVzdC1zdGF0ZS1tYWNoaW5lLWVucmljaG1lbnQnKTtcbiAgfVxufVxuXG4vLyBUZXN0IGltcGxlbWVudGF0aW9uIHdpdGggcG9zdC1wcm9jZXNzaW5nIHN0ZXBcbmNsYXNzIFRlc3REb2N1bWVudFByb2Nlc3NpbmdXaXRoUG9zdFByb2Nlc3NpbmcgZXh0ZW5kcyBCYXNlRG9jdW1lbnRQcm9jZXNzaW5nIHtcbiAgcHJpdmF0ZSBjbGFzc2lmaWNhdGlvbkZuOiBGdW5jdGlvbjtcbiAgcHJpdmF0ZSBwcm9jZXNzaW5nRm46IEZ1bmN0aW9uO1xuICBwcml2YXRlIHBvc3RQcm9jZXNzaW5nRm46IEZ1bmN0aW9uO1xuXG4gIGNvbnN0cnVjdG9yKHNjb3BlOiBhbnksIGlkOiBzdHJpbmcsIHByb3BzOiBhbnkpIHtcbiAgICBzdXBlcihzY29wZSwgaWQsIHByb3BzKTtcblxuICAgIHRoaXMuY2xhc3NpZmljYXRpb25GbiA9IG5ldyBGdW5jdGlvbih0aGlzLCAnQ2xhc3NpZmljYXRpb25GbicsIHtcbiAgICAgIHJ1bnRpbWU6IFJ1bnRpbWUuTk9ERUpTXzIwX1gsXG4gICAgICBoYW5kbGVyOiAnaW5kZXguaGFuZGxlcicsXG4gICAgICBjb2RlOiBDb2RlLmZyb21JbmxpbmUoJ2V4cG9ydHMuaGFuZGxlciA9IGFzeW5jICgpID0+ICh7IGRvY3VtZW50Q2xhc3NpZmljYXRpb246IFwiVEVTVFwiIH0pOycpLFxuICAgIH0pO1xuXG4gICAgdGhpcy5wcm9jZXNzaW5nRm4gPSBuZXcgRnVuY3Rpb24odGhpcywgJ1Byb2Nlc3NpbmdGbicsIHtcbiAgICAgIHJ1bnRpbWU6IFJ1bnRpbWUuTk9ERUpTXzIwX1gsXG4gICAgICBoYW5kbGVyOiAnaW5kZXguaGFuZGxlcicsXG4gICAgICBjb2RlOiBDb2RlLmZyb21JbmxpbmUoJ2V4cG9ydHMuaGFuZGxlciA9IGFzeW5jICgpID0+ICh7IHJlc3VsdDoge30gfSk7JyksXG4gICAgfSk7XG5cbiAgICB0aGlzLnBvc3RQcm9jZXNzaW5nRm4gPSBuZXcgRnVuY3Rpb24odGhpcywgJ1Bvc3RQcm9jZXNzaW5nRm4nLCB7XG4gICAgICBydW50aW1lOiBSdW50aW1lLk5PREVKU18yMF9YLFxuICAgICAgaGFuZGxlcjogJ2luZGV4LmhhbmRsZXInLFxuICAgICAgY29kZTogQ29kZS5mcm9tSW5saW5lKCdleHBvcnRzLmhhbmRsZXIgPSBhc3luYyAoKSA9PiAoeyBwcm9jZXNzZWQ6IHRydWUgfSk7JyksXG4gICAgfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgY2xhc3NpZmljYXRpb25TdGVwKCk6IERvY3VtZW50UHJvY2Vzc2luZ1N0ZXBUeXBlIHtcbiAgICByZXR1cm4gbmV3IExhbWJkYUludm9rZSh0aGlzLCAnTW9ja0NsYXNzaWZpY2F0aW9uJywge1xuICAgICAgbGFtYmRhRnVuY3Rpb246IHRoaXMuY2xhc3NpZmljYXRpb25GbixcbiAgICAgIHJlc3VsdFBhdGg6ICckLmNsYXNzaWZpY2F0aW9uUmVzdWx0JyxcbiAgICB9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBwcm9jZXNzaW5nU3RlcCgpOiBEb2N1bWVudFByb2Nlc3NpbmdTdGVwVHlwZSB7XG4gICAgcmV0dXJuIG5ldyBMYW1iZGFJbnZva2UodGhpcywgJ01vY2tQcm9jZXNzaW5nJywge1xuICAgICAgbGFtYmRhRnVuY3Rpb246IHRoaXMucHJvY2Vzc2luZ0ZuLFxuICAgICAgcmVzdWx0UGF0aDogJyQucHJvY2Vzc2luZ1Jlc3VsdCcsXG4gICAgfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgZW5yaWNobWVudFN0ZXAoKTogRG9jdW1lbnRQcm9jZXNzaW5nU3RlcFR5cGUgfCB1bmRlZmluZWQge1xuICAgIHJldHVybiB1bmRlZmluZWQ7XG4gIH1cblxuICBwcm90ZWN0ZWQgcG9zdFByb2Nlc3NpbmdTdGVwKCk6IERvY3VtZW50UHJvY2Vzc2luZ1N0ZXBUeXBlIHwgdW5kZWZpbmVkIHtcbiAgICByZXR1cm4gbmV3IExhbWJkYUludm9rZSh0aGlzLCAnTW9ja1Bvc3RQcm9jZXNzaW5nJywge1xuICAgICAgbGFtYmRhRnVuY3Rpb246IHRoaXMucG9zdFByb2Nlc3NpbmdGbixcbiAgICAgIHJlc3VsdFBhdGg6ICckLnBvc3RQcm9jZXNzZWRSZXN1bHQnLFxuICAgIH0pO1xuICB9XG5cbiAgcHVibGljIGNyZWF0ZVN0YXRlTWFjaGluZSgpIHtcbiAgICByZXR1cm4gdGhpcy5oYW5kbGVTdGF0ZU1hY2hpbmVDcmVhdGlvbigndGVzdC1zdGF0ZS1tYWNoaW5lLXBvc3QtcHJvY2Vzc2luZycpO1xuICB9XG59XG5cbmRlc2NyaWJlKCdCYXNlRG9jdW1lbnRQcm9jZXNzaW5nJywgKCkgPT4ge1xuICBsZXQgbWluaW1hbFN0YWNrOiBTdGFjaztcbiAgbGV0IGN1c3RvbVN0YWNrOiBTdGFjaztcbiAgbGV0IGVucmljaG1lbnRTdGFjazogU3RhY2s7XG4gIGxldCBwb3N0UHJvY2Vzc2luZ1N0YWNrOiBTdGFjaztcbiAgbGV0IGV2ZW50QnJpZGdlU3RhY2s6IFN0YWNrO1xuICBsZXQgbWluaW1hbFRlbXBsYXRlOiBUZW1wbGF0ZTtcbiAgbGV0IGN1c3RvbVRlbXBsYXRlOiBUZW1wbGF0ZTtcbiAgbGV0IGVucmljaG1lbnRUZW1wbGF0ZTogVGVtcGxhdGU7XG4gIGxldCBwb3N0UHJvY2Vzc2luZ1RlbXBsYXRlOiBUZW1wbGF0ZTtcbiAgbGV0IGV2ZW50QnJpZGdlVGVtcGxhdGU6IFRlbXBsYXRlO1xuXG4gIGJlZm9yZUFsbCgoKSA9PiB7XG4gICAgLy8gTWluaW1hbCBjb25maWd1cmF0aW9uXG4gICAgbWluaW1hbFN0YWNrID0gbmV3IFN0YWNrKCk7XG4gICAgY29uc3QgbWluaW1hbENvbnN0cnVjdCA9IG5ldyBUZXN0RG9jdW1lbnRQcm9jZXNzaW5nKG1pbmltYWxTdGFjaywgJ01pbmltYWxUZXN0Jywge30pO1xuICAgIG1pbmltYWxDb25zdHJ1Y3QuY3JlYXRlU3RhdGVNYWNoaW5lKCk7XG5cbiAgICAvLyBDdXN0b20gY29uZmlndXJhdGlvbiB3aXRoIGN1c3RvbSB0YWJsZSwgYnVja2V0LCBhbmQgdGltZW91dFxuICAgIGN1c3RvbVN0YWNrID0gbmV3IFN0YWNrKCk7XG4gICAgY29uc3QgY3VzdG9tVGFibGUgPSBuZXcgVGFibGUoY3VzdG9tU3RhY2ssICdDdXN0b21UYWJsZScsIHtcbiAgICAgIHBhcnRpdGlvbktleTogeyBuYW1lOiAnRG9jdW1lbnRJZCcsIHR5cGU6IEF0dHJpYnV0ZVR5cGUuU1RSSU5HIH0sXG4gICAgfSk7XG4gICAgY29uc3QgY3VzdG9tS2V5ID0gbmV3IEtleShjdXN0b21TdGFjaywgJ0N1c3RvbUtleScsIHsgZW5hYmxlS2V5Um90YXRpb246IHRydWUgfSk7XG4gICAgY29uc3QgYWNjZXNzTG9nID0gbmV3IEFjY2Vzc0xvZyhjdXN0b21TdGFjaywgJ0FjY2Vzc0xvZycpO1xuICAgIGNvbnN0IGN1c3RvbUJ1Y2tldCA9IG5ldyBCdWNrZXQoY3VzdG9tU3RhY2ssICdDdXN0b21CdWNrZXQnLCB7XG4gICAgICBzZXJ2ZXJBY2Nlc3NMb2dzQnVja2V0OiBhY2Nlc3NMb2cuYnVja2V0LFxuICAgICAgc2VydmVyQWNjZXNzTG9nc1ByZWZpeDogYWNjZXNzTG9nLmJ1Y2tldFByZWZpeCxcbiAgICAgIGVuZm9yY2VTU0w6IHRydWUsXG4gICAgfSk7XG4gICAgY29uc3QgY3VzdG9tQWRhcHRlciA9IG5ldyBRdWV1ZWRTM0FkYXB0ZXIoeyBidWNrZXQ6IGN1c3RvbUJ1Y2tldCB9KTtcbiAgICBjb25zdCBjdXN0b21Db25zdHJ1Y3QgPSBuZXcgVGVzdERvY3VtZW50UHJvY2Vzc2luZyhjdXN0b21TdGFjaywgJ0N1c3RvbVRlc3QnLCB7XG4gICAgICBkb2N1bWVudFByb2Nlc3NpbmdUYWJsZTogY3VzdG9tVGFibGUsXG4gICAgICBpbmdyZXNzQWRhcHRlcjogY3VzdG9tQWRhcHRlcixcbiAgICAgIHdvcmtmbG93VGltZW91dDogRHVyYXRpb24ubWludXRlcygzMCksXG4gICAgICByZW1vdmFsUG9saWN5OiBSZW1vdmFsUG9saWN5LlJFVEFJTixcbiAgICAgIGVuY3J5cHRpb25LZXk6IGN1c3RvbUtleSxcbiAgICB9KTtcbiAgICBjdXN0b21Db25zdHJ1Y3QuY3JlYXRlU3RhdGVNYWNoaW5lKCk7XG5cbiAgICAvLyBXaXRoIGVucmljaG1lbnQgc3RlcFxuICAgIGVucmljaG1lbnRTdGFjayA9IG5ldyBTdGFjaygpO1xuICAgIGNvbnN0IGVucmljaG1lbnRDb25zdHJ1Y3QgPSBuZXcgVGVzdERvY3VtZW50UHJvY2Vzc2luZ1dpdGhFbnJpY2htZW50KGVucmljaG1lbnRTdGFjaywgJ0VucmljaG1lbnRUZXN0Jywge30pO1xuICAgIGVucmljaG1lbnRDb25zdHJ1Y3QuY3JlYXRlU3RhdGVNYWNoaW5lKCk7XG5cbiAgICAvLyBXaXRoIHBvc3QtcHJvY2Vzc2luZyBzdGVwXG4gICAgcG9zdFByb2Nlc3NpbmdTdGFjayA9IG5ldyBTdGFjaygpO1xuICAgIGNvbnN0IHBvc3RQcm9jZXNzaW5nQ29uc3RydWN0ID0gbmV3IFRlc3REb2N1bWVudFByb2Nlc3NpbmdXaXRoUG9zdFByb2Nlc3NpbmcocG9zdFByb2Nlc3NpbmdTdGFjaywgJ1Bvc3RQcm9jZXNzaW5nVGVzdCcsIHt9KTtcbiAgICBwb3N0UHJvY2Vzc2luZ0NvbnN0cnVjdC5jcmVhdGVTdGF0ZU1hY2hpbmUoKTtcblxuICAgIC8vIFdpdGggRXZlbnRCcmlkZ2UgYnJva2VyXG4gICAgZXZlbnRCcmlkZ2VTdGFjayA9IG5ldyBTdGFjaygpO1xuICAgIGNvbnN0IGJyb2tlciA9IG5ldyBFdmVudGJyaWRnZUJyb2tlcihldmVudEJyaWRnZVN0YWNrLCAnVGVzdEJyb2tlcicsIHtcbiAgICAgIG5hbWU6ICd0ZXN0LWJyb2tlcicsXG4gICAgICBldmVudFNvdXJjZTogJ3Rlc3Qtc291cmNlJyxcbiAgICB9KTtcbiAgICBjb25zdCBldmVudEJyaWRnZUNvbnN0cnVjdCA9IG5ldyBUZXN0RG9jdW1lbnRQcm9jZXNzaW5nKGV2ZW50QnJpZGdlU3RhY2ssICdFdmVudEJyaWRnZVRlc3QnLCB7XG4gICAgICBldmVudGJyaWRnZUJyb2tlcjogYnJva2VyLFxuICAgIH0pO1xuICAgIGV2ZW50QnJpZGdlQ29uc3RydWN0LmNyZWF0ZVN0YXRlTWFjaGluZSgpO1xuXG4gICAgLy8gR2VuZXJhdGUgdGVtcGxhdGVzIG9uY2VcbiAgICBtaW5pbWFsVGVtcGxhdGUgPSBUZW1wbGF0ZS5mcm9tU3RhY2sobWluaW1hbFN0YWNrKTtcbiAgICBjdXN0b21UZW1wbGF0ZSA9IFRlbXBsYXRlLmZyb21TdGFjayhjdXN0b21TdGFjayk7XG4gICAgZW5yaWNobWVudFRlbXBsYXRlID0gVGVtcGxhdGUuZnJvbVN0YWNrKGVucmljaG1lbnRTdGFjayk7XG4gICAgcG9zdFByb2Nlc3NpbmdUZW1wbGF0ZSA9IFRlbXBsYXRlLmZyb21TdGFjayhwb3N0UHJvY2Vzc2luZ1N0YWNrKTtcbiAgICBldmVudEJyaWRnZVRlbXBsYXRlID0gVGVtcGxhdGUuZnJvbVN0YWNrKGV2ZW50QnJpZGdlU3RhY2spO1xuICB9KTtcblxuICBkZXNjcmliZSgnQmFzaWMgZnVuY3Rpb25hbGl0eScsICgpID0+IHtcbiAgICB0ZXN0KCdjcmVhdGVzIGNvbnN0cnVjdCB3aXRoIG1pbmltYWwgY29uZmlndXJhdGlvbicsICgpID0+IHtcbiAgICAgIGV4cGVjdChtaW5pbWFsVGVtcGxhdGUpLnRvQmVEZWZpbmVkKCk7XG4gICAgfSk7XG5cbiAgICB0ZXN0KCdjcmVhdGVzIER5bmFtb0RCIHRhYmxlIHdpdGggY29ycmVjdCBjb25maWd1cmF0aW9uJywgKCkgPT4ge1xuICAgICAgbWluaW1hbFRlbXBsYXRlLmhhc1Jlc291cmNlUHJvcGVydGllcygnQVdTOjpEeW5hbW9EQjo6VGFibGUnLCB7XG4gICAgICAgIEtleVNjaGVtYTogW3tcbiAgICAgICAgICBBdHRyaWJ1dGVOYW1lOiAnRG9jdW1lbnRJZCcsXG4gICAgICAgICAgS2V5VHlwZTogJ0hBU0gnLFxuICAgICAgICB9XSxcbiAgICAgICAgQmlsbGluZ01vZGU6ICdQQVlfUEVSX1JFUVVFU1QnLFxuICAgICAgICBQb2ludEluVGltZVJlY292ZXJ5U3BlY2lmaWNhdGlvbjoge1xuICAgICAgICAgIFBvaW50SW5UaW1lUmVjb3ZlcnlFbmFibGVkOiB0cnVlLFxuICAgICAgICB9LFxuICAgICAgICBTU0VTcGVjaWZpY2F0aW9uOiB7XG4gICAgICAgICAgU1NFRW5hYmxlZDogdHJ1ZSxcbiAgICAgICAgICBTU0VUeXBlOiAnS01TJyxcbiAgICAgICAgfSxcbiAgICAgIH0pO1xuICAgIH0pO1xuXG4gICAgdGVzdCgnY3JlYXRlcyBLTVMgZW5jcnlwdGlvbiBrZXkgd2l0aCByb3RhdGlvbiBlbmFibGVkJywgKCkgPT4ge1xuICAgICAgbWluaW1hbFRlbXBsYXRlLmhhc1Jlc291cmNlUHJvcGVydGllcygnQVdTOjpLTVM6OktleScsIHtcbiAgICAgICAgRW5hYmxlS2V5Um90YXRpb246IHRydWUsXG4gICAgICB9KTtcbiAgICB9KTtcblxuICAgIHRlc3QoJ2NyZWF0ZXMgUzMgYnVja2V0IGZvciBkb2N1bWVudCBzdG9yYWdlJywgKCkgPT4ge1xuICAgICAgbWluaW1hbFRlbXBsYXRlLmhhc1Jlc291cmNlUHJvcGVydGllcygnQVdTOjpTMzo6QnVja2V0Jywge1xuICAgICAgICBCdWNrZXRFbmNyeXB0aW9uOiB7XG4gICAgICAgICAgU2VydmVyU2lkZUVuY3J5cHRpb25Db25maWd1cmF0aW9uOiBbe1xuICAgICAgICAgICAgU2VydmVyU2lkZUVuY3J5cHRpb25CeURlZmF1bHQ6IHtcbiAgICAgICAgICAgICAgU1NFQWxnb3JpdGhtOiAnYXdzOmttcycsXG4gICAgICAgICAgICB9LFxuICAgICAgICAgIH1dLFxuICAgICAgICB9LFxuICAgICAgfSk7XG4gICAgfSk7XG5cbiAgICB0ZXN0KCdjcmVhdGVzIFNRUyBxdWV1ZSB3aXRoIERMUScsICgpID0+IHtcbiAgICAgIG1pbmltYWxUZW1wbGF0ZS5oYXNSZXNvdXJjZVByb3BlcnRpZXMoJ0FXUzo6U1FTOjpRdWV1ZScsIHtcbiAgICAgICAgUmVkcml2ZVBvbGljeTogTWF0Y2gub2JqZWN0TGlrZSh7XG4gICAgICAgICAgbWF4UmVjZWl2ZUNvdW50OiA1LFxuICAgICAgICB9KSxcbiAgICAgIH0pO1xuICAgIH0pO1xuXG4gICAgdGVzdCgnY3JlYXRlcyBTdGVwIEZ1bmN0aW9ucyBzdGF0ZSBtYWNoaW5lJywgKCkgPT4ge1xuICAgICAgbWluaW1hbFRlbXBsYXRlLnJlc291cmNlQ291bnRJcygnQVdTOjpTdGVwRnVuY3Rpb25zOjpTdGF0ZU1hY2hpbmUnLCAxKTtcbiAgICB9KTtcbiAgfSk7XG5cbiAgZGVzY3JpYmUoJ1N0YXRlIG1hY2hpbmUgY29uZmlndXJhdGlvbicsICgpID0+IHtcbiAgICB0ZXN0KCdjcmVhdGVzIHN0YXRlIG1hY2hpbmUgd2l0aCB0aW1lb3V0JywgKCkgPT4ge1xuICAgICAgbWluaW1hbFRlbXBsYXRlLnJlc291cmNlQ291bnRJcygnQVdTOjpTdGVwRnVuY3Rpb25zOjpTdGF0ZU1hY2hpbmUnLCAxKTtcbiAgICB9KTtcblxuICAgIHRlc3QoJ2NyZWF0ZXMgc3RhdGUgbWFjaGluZSB3aXRoIGN1c3RvbSB0aW1lb3V0JywgKCkgPT4ge1xuICAgICAgY3VzdG9tVGVtcGxhdGUucmVzb3VyY2VDb3VudElzKCdBV1M6OlN0ZXBGdW5jdGlvbnM6OlN0YXRlTWFjaGluZScsIDEpO1xuICAgIH0pO1xuXG4gICAgdGVzdCgnY3JlYXRlcyBzdGF0ZSBtYWNoaW5lIHdpdGggZW5jcnlwdGlvbicsICgpID0+IHtcbiAgICAgIG1pbmltYWxUZW1wbGF0ZS5oYXNSZXNvdXJjZVByb3BlcnRpZXMoJ0FXUzo6U3RlcEZ1bmN0aW9uczo6U3RhdGVNYWNoaW5lJywge1xuICAgICAgICBFbmNyeXB0aW9uQ29uZmlndXJhdGlvbjoge1xuICAgICAgICAgIFR5cGU6ICdDVVNUT01FUl9NQU5BR0VEX0tNU19LRVknLFxuICAgICAgICB9LFxuICAgICAgfSk7XG4gICAgfSk7XG5cbiAgICB0ZXN0KCdjcmVhdGVzIHN0YXRlIG1hY2hpbmUgcm9sZSB3aXRoIER5bmFtb0RCIHBlcm1pc3Npb25zJywgKCkgPT4ge1xuICAgICAgbWluaW1hbFRlbXBsYXRlLmhhc1Jlc291cmNlUHJvcGVydGllcygnQVdTOjpJQU06OlJvbGUnLCB7XG4gICAgICAgIEFzc3VtZVJvbGVQb2xpY3lEb2N1bWVudDoge1xuICAgICAgICAgIFN0YXRlbWVudDogW3tcbiAgICAgICAgICAgIEFjdGlvbjogJ3N0czpBc3N1bWVSb2xlJyxcbiAgICAgICAgICAgIEVmZmVjdDogJ0FsbG93JyxcbiAgICAgICAgICAgIFByaW5jaXBhbDoge1xuICAgICAgICAgICAgICBTZXJ2aWNlOiAnc3RhdGVzLmFtYXpvbmF3cy5jb20nLFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICB9XSxcbiAgICAgICAgfSxcbiAgICAgIH0pO1xuXG4gICAgICBtaW5pbWFsVGVtcGxhdGUuaGFzUmVzb3VyY2VQcm9wZXJ0aWVzKCdBV1M6OklBTTo6UG9saWN5Jywge1xuICAgICAgICBQb2xpY3lEb2N1bWVudDoge1xuICAgICAgICAgIFN0YXRlbWVudDogTWF0Y2guYXJyYXlXaXRoKFtcbiAgICAgICAgICAgIE1hdGNoLm9iamVjdExpa2Uoe1xuICAgICAgICAgICAgICBFZmZlY3Q6ICdBbGxvdycsXG4gICAgICAgICAgICAgIFJlc291cmNlOiBNYXRjaC5hbnlWYWx1ZSgpLFxuICAgICAgICAgICAgfSksXG4gICAgICAgICAgXSksXG4gICAgICAgIH0sXG4gICAgICB9KTtcbiAgICB9KTtcbiAgfSk7XG5cbiAgZGVzY3JpYmUoJ0Vycm9yIGhhbmRsaW5nIGNoYWlucycsICgpID0+IHtcbiAgICB0ZXN0KCdpbmNsdWRlcyBEeW5hbW9EQiB1cGRhdGUgZm9yIGNsYXNzaWZpY2F0aW9uIGZhaWx1cmUnLCAoKSA9PiB7XG4gICAgICBtaW5pbWFsVGVtcGxhdGUuaGFzUmVzb3VyY2VQcm9wZXJ0aWVzKCdBV1M6OlN0ZXBGdW5jdGlvbnM6OlN0YXRlTWFjaGluZScsIHtcbiAgICAgICAgRGVmaW5pdGlvblN0cmluZzogTWF0Y2gub2JqZWN0TGlrZSh7XG4gICAgICAgICAgJ0ZuOjpKb2luJzogTWF0Y2guYXJyYXlXaXRoKFsnJ10pLFxuICAgICAgICB9KSxcbiAgICAgIH0pO1xuICAgIH0pO1xuXG4gICAgdGVzdCgnaW5jbHVkZXMgRHluYW1vREIgdXBkYXRlIGZvciBwcm9jZXNzaW5nIGZhaWx1cmUnLCAoKSA9PiB7XG4gICAgICBtaW5pbWFsVGVtcGxhdGUuaGFzUmVzb3VyY2VQcm9wZXJ0aWVzKCdBV1M6OlN0ZXBGdW5jdGlvbnM6OlN0YXRlTWFjaGluZScsIHtcbiAgICAgICAgRGVmaW5pdGlvblN0cmluZzogTWF0Y2gub2JqZWN0TGlrZSh7XG4gICAgICAgICAgJ0ZuOjpKb2luJzogTWF0Y2guYXJyYXlXaXRoKFsnJ10pLFxuICAgICAgICB9KSxcbiAgICAgIH0pO1xuICAgIH0pO1xuXG4gICAgdGVzdCgnaW5jbHVkZXMgd29ya2Zsb3cgc3RhdHVzIHVwZGF0ZXMgZm9yIHN1Y2Nlc3MnLCAoKSA9PiB7XG4gICAgICBtaW5pbWFsVGVtcGxhdGUuaGFzUmVzb3VyY2VQcm9wZXJ0aWVzKCdBV1M6OlN0ZXBGdW5jdGlvbnM6OlN0YXRlTWFjaGluZScsIHtcbiAgICAgICAgRGVmaW5pdGlvblN0cmluZzogTWF0Y2gub2JqZWN0TGlrZSh7XG4gICAgICAgICAgJ0ZuOjpKb2luJzogTWF0Y2guYXJyYXlXaXRoKFsnJ10pLFxuICAgICAgICB9KSxcbiAgICAgIH0pO1xuICAgIH0pO1xuICB9KTtcblxuICBkZXNjcmliZSgnT3B0aW9uYWwgd29ya2Zsb3cgc3RlcHMnLCAoKSA9PiB7XG4gICAgdGVzdCgnaW5jbHVkZXMgZW5yaWNobWVudCBzdGVwIHdoZW4gcHJvdmlkZWQnLCAoKSA9PiB7XG4gICAgICBlbnJpY2htZW50VGVtcGxhdGUuaGFzUmVzb3VyY2VQcm9wZXJ0aWVzKCdBV1M6OlN0ZXBGdW5jdGlvbnM6OlN0YXRlTWFjaGluZScsIHtcbiAgICAgICAgRGVmaW5pdGlvblN0cmluZzogTWF0Y2gub2JqZWN0TGlrZSh7XG4gICAgICAgICAgJ0ZuOjpKb2luJzogTWF0Y2guYXJyYXlXaXRoKFsnJ10pLFxuICAgICAgICB9KSxcbiAgICAgIH0pO1xuICAgIH0pO1xuXG4gICAgdGVzdCgnaW5jbHVkZXMgcG9zdC1wcm9jZXNzaW5nIHN0ZXAgd2hlbiBwcm92aWRlZCcsICgpID0+IHtcbiAgICAgIHBvc3RQcm9jZXNzaW5nVGVtcGxhdGUuaGFzUmVzb3VyY2VQcm9wZXJ0aWVzKCdBV1M6OlN0ZXBGdW5jdGlvbnM6OlN0YXRlTWFjaGluZScsIHtcbiAgICAgICAgRGVmaW5pdGlvblN0cmluZzogTWF0Y2gub2JqZWN0TGlrZSh7XG4gICAgICAgICAgJ0ZuOjpKb2luJzogTWF0Y2guYXJyYXlXaXRoKFsnJ10pLFxuICAgICAgICB9KSxcbiAgICAgIH0pO1xuICAgIH0pO1xuXG4gICAgdGVzdCgnbWFya3Mgd29ya2Zsb3cgY29tcGxldGUgYWZ0ZXIgcHJvY2Vzc2luZyB3aGVuIG5vIG9wdGlvbmFsIHN0ZXBzJywgKCkgPT4ge1xuICAgICAgbWluaW1hbFRlbXBsYXRlLmhhc1Jlc291cmNlUHJvcGVydGllcygnQVdTOjpTdGVwRnVuY3Rpb25zOjpTdGF0ZU1hY2hpbmUnLCB7XG4gICAgICAgIERlZmluaXRpb25TdHJpbmc6IE1hdGNoLm9iamVjdExpa2Uoe1xuICAgICAgICAgICdGbjo6Sm9pbic6IE1hdGNoLmFycmF5V2l0aChbJyddKSxcbiAgICAgICAgfSksXG4gICAgICB9KTtcbiAgICB9KTtcbiAgfSk7XG5cbiAgZGVzY3JpYmUoJ0N1c3RvbSBjb25maWd1cmF0aW9uJywgKCkgPT4ge1xuICAgIHRlc3QoJ3VzZXMgcHJvdmlkZWQgRHluYW1vREIgdGFibGUnLCAoKSA9PiB7XG4gICAgICBjdXN0b21UZW1wbGF0ZS5yZXNvdXJjZUNvdW50SXMoJ0FXUzo6RHluYW1vREI6OlRhYmxlJywgMSk7XG4gICAgICBjdXN0b21UZW1wbGF0ZS5oYXNSZXNvdXJjZVByb3BlcnRpZXMoJ0FXUzo6RHluYW1vREI6OlRhYmxlJywge1xuICAgICAgICBLZXlTY2hlbWE6IFt7XG4gICAgICAgICAgQXR0cmlidXRlTmFtZTogJ0RvY3VtZW50SWQnLFxuICAgICAgICAgIEtleVR5cGU6ICdIQVNIJyxcbiAgICAgICAgfV0sXG4gICAgICB9KTtcbiAgICB9KTtcblxuICAgIHRlc3QoJ3VzZXMgcHJvdmlkZWQgZW5jcnlwdGlvbiBrZXknLCAoKSA9PiB7XG4gICAgICBjdXN0b21UZW1wbGF0ZS5yZXNvdXJjZUNvdW50SXMoJ0FXUzo6S01TOjpLZXknLCAyKTsgLy8gQ3VzdG9tIGtleSArIGFkYXB0ZXIga2V5XG4gICAgfSk7XG5cbiAgICB0ZXN0KCd1c2VzIHByb3ZpZGVkIFMzIGJ1Y2tldCB2aWEgYWRhcHRlcicsICgpID0+IHtcbiAgICAgIGN1c3RvbVRlbXBsYXRlLmhhc1Jlc291cmNlUHJvcGVydGllcygnQVdTOjpTMzo6QnVja2V0Jywge1xuICAgICAgICBCdWNrZXRFbmNyeXB0aW9uOiBNYXRjaC5vYmplY3RMaWtlKHtcbiAgICAgICAgICBTZXJ2ZXJTaWRlRW5jcnlwdGlvbkNvbmZpZ3VyYXRpb246IE1hdGNoLmFueVZhbHVlKCksXG4gICAgICAgIH0pLFxuICAgICAgfSk7XG4gICAgfSk7XG5cbiAgICB0ZXN0KCdhcHBsaWVzIGN1c3RvbSByZW1vdmFsIHBvbGljeScsICgpID0+IHtcbiAgICAgIGN1c3RvbVRlbXBsYXRlLmhhc1Jlc291cmNlKCdBV1M6OkR5bmFtb0RCOjpUYWJsZScsIHtcbiAgICAgICAgRGVsZXRpb25Qb2xpY3k6ICdSZXRhaW4nLFxuICAgICAgICBVcGRhdGVSZXBsYWNlUG9saWN5OiAnUmV0YWluJyxcbiAgICAgIH0pO1xuICAgIH0pO1xuICB9KTtcblxuICBkZXNjcmliZSgnRXZlbnRCcmlkZ2UgaW50ZWdyYXRpb24nLCAoKSA9PiB7XG4gICAgdGVzdCgnaW5jbHVkZXMgRXZlbnRCcmlkZ2UgZXZlbnQgYnVzIHdoZW4gYnJva2VyIHByb3ZpZGVkJywgKCkgPT4ge1xuICAgICAgZXZlbnRCcmlkZ2VUZW1wbGF0ZS5oYXNSZXNvdXJjZVByb3BlcnRpZXMoJ0FXUzo6RXZlbnRzOjpFdmVudEJ1cycsIHtcbiAgICAgICAgTmFtZTogJ3Rlc3QtYnJva2VyJyxcbiAgICAgIH0pO1xuICAgIH0pO1xuXG4gICAgdGVzdCgnaW5jbHVkZXMgRXZlbnRCcmlkZ2UgcHV0IGV2ZW50cyBpbiBzdGF0ZSBtYWNoaW5lJywgKCkgPT4ge1xuICAgICAgZXZlbnRCcmlkZ2VUZW1wbGF0ZS5oYXNSZXNvdXJjZVByb3BlcnRpZXMoJ0FXUzo6U3RlcEZ1bmN0aW9uczo6U3RhdGVNYWNoaW5lJywge1xuICAgICAgICBEZWZpbml0aW9uU3RyaW5nOiBNYXRjaC5vYmplY3RMaWtlKHtcbiAgICAgICAgICAnRm46OkpvaW4nOiBNYXRjaC5hcnJheVdpdGgoWycnXSksXG4gICAgICAgIH0pLFxuICAgICAgfSk7XG4gICAgfSk7XG4gIH0pO1xuXG4gIGRlc2NyaWJlKCdTZWN1cml0eScsICgpID0+IHtcbiAgICB0ZXN0KCdlbmNyeXB0cyBEeW5hbW9EQiB0YWJsZSB3aXRoIEtNUycsICgpID0+IHtcbiAgICAgIG1pbmltYWxUZW1wbGF0ZS5oYXNSZXNvdXJjZVByb3BlcnRpZXMoJ0FXUzo6RHluYW1vREI6OlRhYmxlJywge1xuICAgICAgICBTU0VTcGVjaWZpY2F0aW9uOiB7XG4gICAgICAgICAgU1NFRW5hYmxlZDogdHJ1ZSxcbiAgICAgICAgICBTU0VUeXBlOiAnS01TJyxcbiAgICAgICAgfSxcbiAgICAgIH0pO1xuICAgIH0pO1xuXG4gICAgdGVzdCgnZW5jcnlwdHMgUzMgYnVja2V0IHdpdGggS01TJywgKCkgPT4ge1xuICAgICAgbWluaW1hbFRlbXBsYXRlLmhhc1Jlc291cmNlUHJvcGVydGllcygnQVdTOjpTMzo6QnVja2V0Jywge1xuICAgICAgICBCdWNrZXRFbmNyeXB0aW9uOiB7XG4gICAgICAgICAgU2VydmVyU2lkZUVuY3J5cHRpb25Db25maWd1cmF0aW9uOiBbe1xuICAgICAgICAgICAgU2VydmVyU2lkZUVuY3J5cHRpb25CeURlZmF1bHQ6IHtcbiAgICAgICAgICAgICAgU1NFQWxnb3JpdGhtOiAnYXdzOmttcycsXG4gICAgICAgICAgICB9LFxuICAgICAgICAgIH1dLFxuICAgICAgICB9LFxuICAgICAgfSk7XG4gICAgfSk7XG5cbiAgICB0ZXN0KCdlbmNyeXB0cyBTUVMgcXVldWUgd2l0aCBLTVMnLCAoKSA9PiB7XG4gICAgICBtaW5pbWFsVGVtcGxhdGUuaGFzUmVzb3VyY2VQcm9wZXJ0aWVzKCdBV1M6OlNRUzo6UXVldWUnLCB7XG4gICAgICAgIEttc01hc3RlcktleUlkOiBNYXRjaC5hbnlWYWx1ZSgpLFxuICAgICAgfSk7XG4gICAgfSk7XG5cbiAgICB0ZXN0KCdlbmNyeXB0cyBzdGF0ZSBtYWNoaW5lIHdpdGggY3VzdG9tZXIgbWFuYWdlZCBrZXknLCAoKSA9PiB7XG4gICAgICBtaW5pbWFsVGVtcGxhdGUuaGFzUmVzb3VyY2VQcm9wZXJ0aWVzKCdBV1M6OlN0ZXBGdW5jdGlvbnM6OlN0YXRlTWFjaGluZScsIHtcbiAgICAgICAgRW5jcnlwdGlvbkNvbmZpZ3VyYXRpb246IHtcbiAgICAgICAgICBUeXBlOiAnQ1VTVE9NRVJfTUFOQUdFRF9LTVNfS0VZJyxcbiAgICAgICAgICBLbXNLZXlJZDogTWF0Y2guYW55VmFsdWUoKSxcbiAgICAgICAgfSxcbiAgICAgIH0pO1xuICAgIH0pO1xuXG4gICAgdGVzdCgnZW5mb3JjZXMgU1NMIG9uIFMzIGJ1Y2tldCcsICgpID0+IHtcbiAgICAgIG1pbmltYWxUZW1wbGF0ZS5oYXNSZXNvdXJjZVByb3BlcnRpZXMoJ0FXUzo6UzM6OkJ1Y2tldFBvbGljeScsIHtcbiAgICAgICAgUG9saWN5RG9jdW1lbnQ6IHtcbiAgICAgICAgICBTdGF0ZW1lbnQ6IE1hdGNoLmFycmF5V2l0aChbXG4gICAgICAgICAgICBNYXRjaC5vYmplY3RMaWtlKHtcbiAgICAgICAgICAgICAgRWZmZWN0OiAnRGVueScsXG4gICAgICAgICAgICAgIENvbmRpdGlvbjoge1xuICAgICAgICAgICAgICAgIEJvb2w6IHtcbiAgICAgICAgICAgICAgICAgICdhd3M6U2VjdXJlVHJhbnNwb3J0JzogJ2ZhbHNlJyxcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgfSksXG4gICAgICAgICAgXSksXG4gICAgICAgIH0sXG4gICAgICB9KTtcbiAgICB9KTtcblxuICAgIHRlc3QoJ2VuZm9yY2VzIFNTTCBvbiBTUVMgcXVldWUnLCAoKSA9PiB7XG4gICAgICBtaW5pbWFsVGVtcGxhdGUuaGFzUmVzb3VyY2VQcm9wZXJ0aWVzKCdBV1M6OlNRUzo6UXVldWVQb2xpY3knLCB7XG4gICAgICAgIFBvbGljeURvY3VtZW50OiB7XG4gICAgICAgICAgU3RhdGVtZW50OiBNYXRjaC5hcnJheVdpdGgoW1xuICAgICAgICAgICAgTWF0Y2gub2JqZWN0TGlrZSh7XG4gICAgICAgICAgICAgIEVmZmVjdDogJ0RlbnknLFxuICAgICAgICAgICAgICBDb25kaXRpb246IHtcbiAgICAgICAgICAgICAgICBCb29sOiB7XG4gICAgICAgICAgICAgICAgICAnYXdzOlNlY3VyZVRyYW5zcG9ydCc6ICdmYWxzZScsXG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIH0pLFxuICAgICAgICAgIF0pLFxuICAgICAgICB9LFxuICAgICAgfSk7XG4gICAgfSk7XG4gIH0pO1xuXG4gIGRlc2NyaWJlKCdBZGFwdGVyIGludGVncmF0aW9uJywgKCkgPT4ge1xuICAgIHRlc3QoJ2NyZWF0ZXMgUzMgZXZlbnQgbm90aWZpY2F0aW9uIHRvIFNRUycsICgpID0+IHtcbiAgICAgIG1pbmltYWxUZW1wbGF0ZS5oYXNSZXNvdXJjZVByb3BlcnRpZXMoJ0FXUzo6TGFtYmRhOjpGdW5jdGlvbicsIHtcbiAgICAgICAgSGFuZGxlcjogJ2luZGV4LmhhbmRsZXInLFxuICAgICAgICBFbnZpcm9ubWVudDoge1xuICAgICAgICAgIFZhcmlhYmxlczoge1xuICAgICAgICAgICAgU1RBVEVfTUFDSElORV9BUk46IE1hdGNoLmFueVZhbHVlKCksXG4gICAgICAgICAgICBSQVdfUFJFRklYOiAncmF3LycsXG4gICAgICAgICAgfSxcbiAgICAgICAgfSxcbiAgICAgIH0pO1xuICAgIH0pO1xuXG4gICAgdGVzdCgnY3JlYXRlcyBTUVMgY29uc3VtZXIgTGFtYmRhIHdpdGggZXZlbnQgc291cmNlJywgKCkgPT4ge1xuICAgICAgbWluaW1hbFRlbXBsYXRlLmhhc1Jlc291cmNlUHJvcGVydGllcygnQVdTOjpMYW1iZGE6OkV2ZW50U291cmNlTWFwcGluZycsIHtcbiAgICAgICAgQmF0Y2hTaXplOiAxMCxcbiAgICAgICAgRXZlbnRTb3VyY2VBcm46IE1hdGNoLmFueVZhbHVlKCksXG4gICAgICAgIEZ1bmN0aW9uTmFtZTogTWF0Y2guYW55VmFsdWUoKSxcbiAgICAgIH0pO1xuICAgIH0pO1xuXG4gICAgdGVzdCgnY3JlYXRlcyBkZWFkIGxldHRlciBxdWV1ZSBmb3IgZmFpbGVkIG1lc3NhZ2VzJywgKCkgPT4ge1xuICAgICAgbWluaW1hbFRlbXBsYXRlLnJlc291cmNlQ291bnRJcygnQVdTOjpTUVM6OlF1ZXVlJywgMik7XG4gICAgfSk7XG4gIH0pO1xuXG4gIGRlc2NyaWJlKCdPYnNlcnZhYmlsaXR5JywgKCkgPT4ge1xuICAgIHRlc3QoJ2NyZWF0ZXMgY29uc3RydWN0IHdpdGhvdXQgb2JzZXJ2YWJpbGl0eSBieSBkZWZhdWx0JywgKCkgPT4ge1xuICAgICAgY29uc3Qgc3RhY2sgPSBuZXcgU3RhY2soKTtcbiAgICAgIGNvbnN0IGNvbnN0cnVjdCA9IG5ldyBUZXN0RG9jdW1lbnRQcm9jZXNzaW5nKHN0YWNrLCAnTm9PYnNlcnZhYmlsaXR5Jywge1xuICAgICAgICBlbmFibGVPYnNlcnZhYmlsaXR5OiBmYWxzZSxcbiAgICAgIH0pO1xuICAgICAgY29uc3RydWN0LmNyZWF0ZVN0YXRlTWFjaGluZSgpO1xuICAgICAgY29uc3QgdGVtcGxhdGUgPSBUZW1wbGF0ZS5mcm9tU3RhY2soc3RhY2spO1xuXG4gICAgICB0ZW1wbGF0ZS5oYXNSZXNvdXJjZVByb3BlcnRpZXMoJ0FXUzo6U3RlcEZ1bmN0aW9uczo6U3RhdGVNYWNoaW5lJywge1xuICAgICAgICBMb2dnaW5nQ29uZmlndXJhdGlvbjogTWF0Y2guYWJzZW50KCksXG4gICAgICB9KTtcbiAgICB9KTtcblxuICAgIHRlc3QoJ2VuYWJsZXMgb2JzZXJ2YWJpbGl0eSB3aGVuIGNvbmZpZ3VyZWQnLCAoKSA9PiB7XG4gICAgICBjb25zdCBzdGFjayA9IG5ldyBTdGFjaygpO1xuICAgICAgY29uc3QgY29uc3RydWN0ID0gbmV3IFRlc3REb2N1bWVudFByb2Nlc3Npbmcoc3RhY2ssICdXaXRoT2JzZXJ2YWJpbGl0eScsIHtcbiAgICAgICAgZW5hYmxlT2JzZXJ2YWJpbGl0eTogdHJ1ZSxcbiAgICAgIH0pO1xuICAgICAgY29uc3RydWN0LmNyZWF0ZVN0YXRlTWFjaGluZSgpO1xuICAgICAgY29uc3QgdGVtcGxhdGUgPSBUZW1wbGF0ZS5mcm9tU3RhY2soc3RhY2spO1xuXG4gICAgICB0ZW1wbGF0ZS5oYXNSZXNvdXJjZVByb3BlcnRpZXMoJ0FXUzo6U3RlcEZ1bmN0aW9uczo6U3RhdGVNYWNoaW5lJywge1xuICAgICAgICBMb2dnaW5nQ29uZmlndXJhdGlvbjogTWF0Y2gub2JqZWN0TGlrZSh7XG4gICAgICAgICAgTGV2ZWw6ICdBTEwnLFxuICAgICAgICB9KSxcbiAgICAgIH0pO1xuICAgIH0pO1xuICB9KTtcblxuICBkZXNjcmliZSgnUmVzb3VyY2UgY291bnRzJywgKCkgPT4ge1xuICAgIHRlc3QoJ2NyZWF0ZXMgZXhwZWN0ZWQgbnVtYmVyIG9mIHJlc291cmNlcycsICgpID0+IHtcbiAgICAgIG1pbmltYWxUZW1wbGF0ZS5yZXNvdXJjZUNvdW50SXMoJ0FXUzo6RHluYW1vREI6OlRhYmxlJywgMSk7XG4gICAgICBtaW5pbWFsVGVtcGxhdGUucmVzb3VyY2VDb3VudElzKCdBV1M6OlMzOjpCdWNrZXQnLCAxKTtcbiAgICAgIG1pbmltYWxUZW1wbGF0ZS5yZXNvdXJjZUNvdW50SXMoJ0FXUzo6U1FTOjpRdWV1ZScsIDIpO1xuICAgICAgbWluaW1hbFRlbXBsYXRlLnJlc291cmNlQ291bnRJcygnQVdTOjpTdGVwRnVuY3Rpb25zOjpTdGF0ZU1hY2hpbmUnLCAxKTtcbiAgICB9KTtcbiAgfSk7XG59KTtcbiJdfQ==
|