UNPKG

29.6 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const ava_1 = require("ava");
4const fs_extra_1 = require("fs-extra");
5const url_1 = require("url");
6const util_1 = require("util");
7const index_1 = require("../index");
8const funcs = require("./fixtures/functions");
9const util_2 = require("./fixtures/util");
10const error_1 = require("../src/error");
11async function testCleanup(t, options) {
12 const m = await (0, index_1.faastLocal)(funcs, {
13 gc: "off",
14 ...options
15 });
16 let done = 0;
17 m.functions
18 .hello("there")
19 .then(_ => done++)
20 .catch(_ => { });
21 m.functions
22 .sleep(1000)
23 .then(_ => done++)
24 .catch(_ => { });
25 await m.cleanup();
26 t.is(done, 0);
27}
28async function testOrder(t, options) {
29 const faastModule = await (0, index_1.faastLocal)(funcs, {
30 gc: "off",
31 ...options
32 });
33 t.plan(2);
34 const a = faastModule.functions.emptyReject();
35 const b = faastModule.functions.sleep(0);
36 t.is(await b, undefined);
37 try {
38 await a;
39 }
40 catch (err) {
41 t.is(err, undefined);
42 }
43 finally {
44 await faastModule.cleanup();
45 }
46}
47async function testConcurrency(t, { options, maxConcurrency, expectedConcurrency }) {
48 const faastModule = await (0, index_1.faastLocal)(funcs, {
49 ...options,
50 gc: "off",
51 concurrency: maxConcurrency
52 });
53 try {
54 const N = maxConcurrency * 2;
55 const promises = [];
56 for (let i = 0; i < N; i++) {
57 promises.push(faastModule.functions.spin(2000));
58 }
59 const timings = await Promise.all(promises);
60 t.is((0, util_2.measureConcurrency)(timings), expectedConcurrency);
61 }
62 finally {
63 await faastModule.cleanup();
64 }
65}
66(0, ava_1.default)("local provider cleanup stops executions", testCleanup, {});
67(0, ava_1.default)("local provider cleanup stops executions with child process", testCleanup, {
68 childProcess: true
69});
70const orderConfigs = [
71 { childProcess: false, concurrency: 1, maxRetries: 0 },
72 { childProcess: true, concurrency: 1, maxRetries: 0 },
73 { childProcess: false, concurrency: 2, maxRetries: 0 },
74 { childProcess: true, concurrency: 2, maxRetries: 0 },
75 { childProcess: false, concurrency: 2, maxRetries: 2 },
76 { childProcess: true, concurrency: 2, maxRetries: 2 }
77];
78for (const config of orderConfigs) {
79 (0, ava_1.default)(`out of order await (async catch) with ${(0, util_1.inspect)(config)}`, testOrder, config);
80}
81async function readFirstLogfile(logDirectoryUrl) {
82 const url = new url_1.URL(logDirectoryUrl);
83 const buf = await (0, fs_extra_1.readFile)(url.pathname + "/0.log");
84 return buf
85 .toString()
86 .split("\n")
87 .map(m => m.replace(/^\[(\d+)\]/, "[$pid]"));
88}
89(0, ava_1.default)("local provider console.log, console.warn, and console.error with child process", async (t) => {
90 const faastModule = await (0, index_1.faastLocal)(funcs, {
91 childProcess: true,
92 concurrency: 1,
93 gc: "off"
94 });
95 try {
96 await faastModule.functions.consoleLog("Remote console.log output");
97 await faastModule.functions.consoleWarn("Remote console.warn output");
98 await faastModule.functions.consoleError("Remote console.error output");
99 await (0, util_2.sleep)(1000);
100 await faastModule.cleanup({ deleteResources: false });
101 const messages = await readFirstLogfile(faastModule.logUrl());
102 t.truthy(messages.find(s => s === "[$pid]: Remote console.log output"));
103 t.truthy(messages.find(s => s === "[$pid]: Remote console.warn output"));
104 t.truthy(messages.find(s => s === "[$pid]: Remote console.error output"));
105 }
106 finally {
107 await faastModule.cleanup({ deleteResources: false });
108 }
109});
110(0, ava_1.default)("local provider log files should be appended, not truncated, after child process crash", async (t) => {
111 const faastModule = await (0, index_1.faastLocal)(funcs, {
112 childProcess: true,
113 concurrency: 1,
114 maxRetries: 1,
115 gc: "off"
116 });
117 try {
118 await faastModule.functions.consoleLog("output 1");
119 try {
120 await faastModule.functions.processExit();
121 }
122 catch (err) { }
123 await faastModule.functions.consoleWarn("output 2");
124 // Wait for flush
125 await (0, util_2.sleep)(500);
126 const messages = await readFirstLogfile(faastModule.logUrl());
127 t.truthy(messages.find(s => s === "[$pid]: output 1"));
128 t.truthy(messages.find(s => s === "[$pid]: output 2"));
129 }
130 finally {
131 await faastModule.cleanup({ deleteResources: false });
132 }
133});
134(0, ava_1.default)("local provider child process exceptions should result in errors with logUrl", async (t) => {
135 const faastModule = await (0, index_1.faastLocal)(funcs, {
136 childProcess: true,
137 concurrency: 1,
138 maxRetries: 1,
139 gc: "off"
140 });
141 t.plan(1);
142 try {
143 await faastModule.functions.error("synthetic error");
144 }
145 catch (err) {
146 const info = error_1.FaastError.info(err);
147 t.true(typeof info.logUrl === "string" && info.logUrl.startsWith(" file:///"), (0, util_1.inspect)(err));
148 }
149 finally {
150 await faastModule.cleanup();
151 }
152});
153(0, ava_1.default)("local provider child process crashes should result in errors with logUrl", async (t) => {
154 const faastModule = await (0, index_1.faastLocal)(funcs, {
155 childProcess: true,
156 concurrency: 1,
157 maxRetries: 1,
158 gc: "off"
159 });
160 t.plan(1);
161 try {
162 await faastModule.functions.processExit(-1);
163 }
164 catch (err) {
165 const info = error_1.FaastError.info(err);
166 t.true(typeof info.logUrl === "string" && info.logUrl.startsWith(" file:///"), (0, util_1.inspect)(err));
167 }
168 finally {
169 await faastModule.cleanup();
170 }
171});
172(0, ava_1.default)("local provider concurrent executions with child processes", async (t) => {
173 await testConcurrency(t, {
174 options: {
175 childProcess: true
176 },
177 maxConcurrency: 5,
178 expectedConcurrency: 5
179 });
180});
181(0, ava_1.default)("local provider no concurrency for cpu bound work without child processes", async (t) => {
182 await testConcurrency(t, {
183 options: {
184 childProcess: false
185 },
186 maxConcurrency: 5,
187 expectedConcurrency: 1
188 });
189});
190(0, ava_1.default)("local provider cleanup waits for all child processes to exit", async (t) => {
191 const faastModule = await (0, index_1.faastLocal)(funcs, {
192 childProcess: true,
193 gc: "off"
194 });
195 faastModule.functions.spin(5000).catch(_ => { });
196 while (true) {
197 await (0, util_2.sleep)(100);
198 if (faastModule.state.executors.length > 0) {
199 break;
200 }
201 }
202 t.is(faastModule.state.executors.length, 1, "executor is not running");
203 await faastModule.cleanup({ gcTimeout: 60 });
204 t.is(faastModule.state.executors.length, 0, "executors are running after cleanup");
205});
206(0, ava_1.default)("local unresolved module", async (t) => {
207 t.plan(1);
208 try {
209 await (0, index_1.faastLocal)({});
210 }
211 catch (err) {
212 t.regex(err.message, /Could not find file/);
213 }
214});
215(0, ava_1.default)("local issue #37", async (t) => {
216 // Previously this code caused an exception about module wrapper not being
217 // re-entrant. The problem was a race condition between wrapper selection
218 // and execution in local provider. Solved by making wrapper selector a
219 // regular function instead of an async function.
220 const m = await (0, index_1.faastLocal)(funcs);
221 try {
222 const { identityString: identity } = m.functions;
223 await identity("a");
224 const b = identity("b");
225 const c = identity("c");
226 await b;
227 await c;
228 // Test succeeds if no exceptions are thrown.
229 t.true(true);
230 }
231 finally {
232 await m.cleanup();
233 }
234});
235//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidW5pdC1sb2NhbC50ZXN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdGVzdC91bml0LWxvY2FsLnRlc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFBQSw2QkFBNkM7QUFDN0MsdUNBQW9DO0FBQ3BDLDZCQUEwQjtBQUMxQiwrQkFBK0I7QUFDL0Isb0NBQW9EO0FBQ3BELDhDQUE4QztBQUM5QywwQ0FBNEQ7QUFDNUQsd0NBQTBDO0FBRTFDLEtBQUssVUFBVSxXQUFXLENBQUMsQ0FBbUIsRUFBRSxPQUFxQjtJQUNqRSxNQUFNLENBQUMsR0FBRyxNQUFNLElBQUEsa0JBQVUsRUFBQyxLQUFLLEVBQUU7UUFDOUIsRUFBRSxFQUFFLEtBQUs7UUFDVCxHQUFHLE9BQU87S0FDYixDQUFDLENBQUM7SUFDSCxJQUFJLElBQUksR0FBRyxDQUFDLENBQUM7SUFFYixDQUFDLENBQUMsU0FBUztTQUNOLEtBQUssQ0FBQyxPQUFPLENBQUM7U0FDZCxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxJQUFJLEVBQUUsQ0FBQztTQUNqQixLQUFLLENBQUMsQ0FBQyxDQUFDLEVBQUUsR0FBRSxDQUFDLENBQUMsQ0FBQztJQUVwQixDQUFDLENBQUMsU0FBUztTQUNOLEtBQUssQ0FBQyxJQUFJLENBQUM7U0FDWCxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxJQUFJLEVBQUUsQ0FBQztTQUNqQixLQUFLLENBQUMsQ0FBQyxDQUFDLEVBQUUsR0FBRSxDQUFDLENBQUMsQ0FBQztJQUVwQixNQUFNLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQztJQUNsQixDQUFDLENBQUMsRUFBRSxDQUFDLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQztBQUNsQixDQUFDO0FBRUQsS0FBSyxVQUFVLFNBQVMsQ0FBQyxDQUFtQixFQUFFLE9BQXFCO0lBQy9ELE1BQU0sV0FBVyxHQUFHLE1BQU0sSUFBQSxrQkFBVSxFQUFDLEtBQUssRUFBRTtRQUN4QyxFQUFF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QSxrQkFBVSxFQUFDLEVBQUUsQ0FBQyxDQUFDO0tBQ3hCO0lBQUMsT0FBTyxHQUFRLEVBQUU7UUFDZixDQUFDLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUscUJBQXFCLENBQUMsQ0FBQztLQUMvQztBQUNMLENBQUMsQ0FBQyxDQUFDO0FBRUgsSUFBQSxhQUFJLEVBQUMsaUJBQWlCLEVBQUUsS0FBSyxFQUFDLENBQUMsRUFBQyxFQUFFO0lBQzlCLDBFQUEwRTtJQUMxRSx5RUFBeUU7SUFDekUsdUVBQXVFO0lBQ3ZFLGlEQUFpRDtJQUNqRCxNQUFNLENBQUMsR0FBRyxNQUFNLElBQUEsa0JBQVUsRUFBQyxLQUFLLENBQUMsQ0FBQztJQUNsQyxJQUFJO1FBQ0EsTUFBTSxFQUFFLGNBQWMsRUFBRSxRQUFRLEVBQUUsR0FBRyxDQUFDLENBQUMsU0FBUyxDQUFDO1FBQ2pELE1BQU0sUUFBUSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQ3BCLE1BQU0sQ0FBQyxHQUFHLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUN4QixNQUFNLENBQUMsR0FBRyxRQUFRLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDeEIsTUFBTSxDQUFDLENBQUM7UUFDUixNQUFNLENBQUMsQ0FBQztRQUNSLDZDQUE2QztRQUM3QyxDQUFDLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO0tBQ2hCO1lBQVM7UUFDTixNQUFNLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQztLQUNyQjtBQUNMLENBQUMsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHRlc3QsIHsgRXhlY3V0aW9uQ29udGV4dCB9IGZyb20gXCJhdmFcIjtcbmltcG9ydCB7IHJlYWRGaWxlIH0gZnJvbSBcImZzLWV4dHJhXCI7XG5pbXBvcnQgeyBVUkwgfSBmcm9tIFwidXJsXCI7XG5pbXBvcnQgeyBpbnNwZWN0IH0gZnJvbSBcInV0aWxcIjtcbmltcG9ydCB7IGZhYXN0TG9jYWwsIExvY2FsT3B0aW9ucyB9IGZyb20gXCIuLi9pbmRleFwiO1xuaW1wb3J0ICogYXMgZnVuY3MgZnJvbSBcIi4vZml4dHVyZXMvZnVuY3Rpb25zXCI7XG5pbXBvcnQgeyBtZWFzdXJlQ29uY3VycmVuY3ksIHNsZWVwIH0gZnJvbSBcIi4vZml4dHVyZXMvdXRpbFwiO1xuaW1wb3J0IHsgRmFhc3RFcnJvciB9IGZyb20gXCIuLi9zcmMvZXJyb3JcIjtcblxuYXN5bmMgZnVuY3Rpb24gdGVzdENsZWFudXAodDogRXhlY3V0aW9uQ29udGV4dCwgb3B0aW9uczogTG9jYWxPcHRpb25zKSB7XG4gICAgY29uc3QgbSA9IGF3YWl0IGZhYXN0TG9jYWwoZnVuY3MsIHtcbiAgICAgICAgZ2M6IFwib2ZmXCIsXG4gICAgICAgIC4uLm9wdGlvbnNcbiAgICB9KTtcbiAgICBsZXQgZG9uZSA9IDA7XG5cbiAgICBtLmZ1bmN0aW9uc1xuICAgICAgICAuaGVsbG8oXCJ0aGVyZVwiKVxuICAgICAgICAudGhlbihfID0+IGRvbmUrKylcbiAgICAgICAgLmNhdGNoKF8gPT4ge30pO1xuXG4gICAgbS5mdW5jdGlvbnNcbiAgICAgICAgLnNsZWVwKDEwMDApXG4gICAgICAgIC50aGVuKF8gPT4gZG9uZSsrKVxuICAgICAgICAuY2F0Y2goXyA9PiB7fSk7XG5cbiAgICBhd2FpdCBtLmNsZWFudXAoKTtcbiAgICB0LmlzKGRvbmUsIDApO1xufVxuXG5hc3luYyBmdW5jdGlvbiB0ZXN0T3JkZXIodDogRXhlY3V0aW9uQ29udGV4dCwgb3B0aW9uczogTG9jYWxPcHRpb25zKSB7XG4gICAgY29uc3QgZmFhc3RNb2R1bGUgPSBhd2FpdCBmYWFzdExvY2FsKGZ1bmNzLCB7XG4gICAgICAgIGdjOiBcIm9mZlwiLFxuICAgICAgICAuLi5vcHRpb25zXG4gICAgfSk7XG4gICAgdC5wbGFuKDIpO1xuXG4gICAgY29uc3QgYSA9IGZhYXN0TW9kdWxlLmZ1bmN0aW9ucy5lbXB0eVJlamVjdCgpO1xuICAgIGNvbnN0IGIgPSBmYWFzdE1vZHVsZS5mdW5jdGlvbnMuc2xlZXAoMCk7XG4gICAgdC5pcyhhd2FpdCBiLCB1bmRlZmluZWQpO1xuICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IGE7XG4gICAgfSBjYXRjaCAoZXJyOiBhbnkpIHtcbiAgICAgICAgdC5pcyhlcnIsIHVuZGVmaW5lZCk7XG4gICAgfSBmaW5hbGx5IHtcbiAgICAgICAgYXdhaXQgZmFhc3RNb2R1bGUuY2xlYW51cCgpO1xuICAgIH1cbn1cblxuYXN5bmMgZnVuY3Rpb24gdGVzdENvbmN1cnJlbmN5KFxuICAgIHQ6IEV4ZWN1dGlvbkNvbnRleHQsXG4gICAge1xuICAgICAgICBvcHRpb25zLFxuICAgICAgICBtYXhDb25jdXJyZW5jeSxcbiAgICAgICAgZXhwZWN0ZWRDb25jdXJyZW5jeVxuICAgIH06IHtcbiAgICAgICAgb3B0aW9uczogTG9jYWxPcHRpb25zO1xuICAgICAgICBtYXhDb25jdXJyZW5jeTogbnVtYmVyO1xuICAgICAgICBleHBlY3RlZENvbmN1cnJlbmN5OiBudW1iZXI7XG4gICAgfVxuKSB7XG4gICAgY29uc3QgZmFhc3RNb2R1bGUgPSBhd2FpdCBmYWFzdExvY2FsKGZ1bmNzLCB7XG4gICAgICAgIC4uLm9wdGlvbnMsXG4gICAgICAgIGdjOiBcIm9mZlwiLFxuICAgICAgICBjb25jdXJyZW5jeTogbWF4Q29uY3VycmVuY3lcbiAgICB9KTtcblxuICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IE4gPSBtYXhDb25jdXJyZW5jeSAqIDI7XG4gICAgICAgIGNvbnN0IHByb21pc2VzID0gW107XG4gICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgTjsgaSsrKSB7XG4gICAgICAgICAgICBwcm9taXNlcy5wdXNoKGZhYXN0TW9kdWxlLmZ1bmN0aW9ucy5zcGluKDIwMDApKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IHRpbWluZ3MgPSBhd2FpdCBQcm9taXNlLmFsbChwcm9taXNlcyk7XG4gICAgICAgIHQuaXMobWVhc3VyZUNvbmN1cnJlbmN5KHRpbWluZ3MpLCBleHBlY3RlZENvbmN1cnJlbmN5KTtcbiAgICB9IGZpbmFsbHkge1xuICAgICAgICBhd2FpdCBmYWFzdE1vZHVsZS5jbGVhbnVwKCk7XG4gICAgfVxufVxuXG50ZXN0KFwibG9jYWwgcHJvdmlkZXIgY2xlYW51cCBzdG9wcyBleGVjdXRpb25zXCIsIHRlc3RDbGVhbnVwLCB7fSk7XG50ZXN0KFwibG9jYWwgcHJvdmlkZXIgY2xlYW51cCBzdG9wcyBleGVjdXRpb25zIHdpdGggY2hpbGQgcHJvY2Vzc1wiLCB0ZXN0Q2xlYW51cCwge1xuICAgIGNoaWxkUHJvY2VzczogdHJ1ZVxufSk7XG5cbmNvbnN0IG9yZGVyQ29uZmlncyA9IFtcbiAgICB7IGNoaWxkUHJvY2VzczogZmFsc2UsIGNvbmN1cnJlbmN5OiAxLCBtYXhSZXRyaWVzOiAwIH0sXG4gICAgeyBjaGlsZFByb2Nlc3M6IHRydWUsIGNvbmN1cnJlbmN5OiAxLCBtYXhSZXRyaWVzOiAwIH0sXG4gICAgeyBjaGlsZFByb2Nlc3M6IGZhbHNlLCBjb25jdXJyZW5jeTogMiwgbWF4UmV0cmllczogMCB9LFxuICAgIHsgY2hpbGRQcm9jZXNzOiB0cnVlLCBjb25jdXJyZW5jeTogMiwgbWF4UmV0cmllczogMCB9LFxuICAgIHsgY2hpbGRQcm9jZXNzOiBmYWxzZSwgY29uY3VycmVuY3k6IDIsIG1heFJldHJpZXM6IDIgfSxcbiAgICB7IGNoaWxkUHJvY2VzczogdHJ1ZSwgY29uY3VycmVuY3k6IDIsIG1heFJldHJpZXM6IDIgfVxuXTtcblxuZm9yIChjb25zdCBjb25maWcgb2Ygb3JkZXJDb25maWdzKSB7XG4gICAgdGVzdChgb3V0IG9mIG9yZGVyIGF3YWl0IChhc3luYyBjYXRjaCkgd2l0aCAke2luc3BlY3QoY29uZmlnKX1gLCB0ZXN0T3JkZXIsIGNvbmZpZyk7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIHJlYWRGaXJzdExvZ2ZpbGUobG9nRGlyZWN0b3J5VXJsOiBzdHJpbmcpIHtcbiAgICBjb25zdCB1cmwgPSBuZXcgVVJMKGxvZ0RpcmVjdG9yeVVybCk7XG4gICAgY29uc3QgYnVmID0gYXdhaXQgcmVhZEZpbGUodXJsLnBhdGhuYW1lICsgXCIvMC5sb2dcIik7XG4gICAgcmV0dXJuIGJ1ZlxuICAgICAgICAudG9TdHJpbmcoKVxuICAgICAgICAuc3BsaXQoXCJcXG5cIilcbiAgICAgICAgLm1hcChtID0+IG0ucmVwbGFjZSgvXlxcWyhcXGQrKVxcXS8sIFwiWyRwaWRdXCIpKTtcbn1cblxudGVzdChcImxvY2FsIHByb3ZpZGVyIGNvbnNvbGUubG9nLCBjb25zb2xlLndhcm4sIGFuZCBjb25zb2xlLmVycm9yIHdpdGggY2hpbGQgcHJvY2Vzc1wiLCBhc3luYyB0ID0+IHtcbiAgICBjb25zdCBmYWFzdE1vZHVsZSA9IGF3YWl0IGZhYXN0TG9jYWwoZnVuY3MsIHtcbiAgICAgICAgY2hpbGRQcm9jZXNzOiB0cnVlLFxuICAgICAgICBjb25jdXJyZW5jeTogMSxcbiAgICAgICAgZ2M6IFwib2ZmXCJcbiAgICB9KTtcbiAgICB0cnkge1xuICAgICAgICBhd2FpdCBmYWFzdE1vZHVsZS5mdW5jdGlvbnMuY29uc29sZUxvZyhcIlJlbW90ZSBjb25zb2xlLmxvZyBvdXRwdXRcIik7XG4gICAgICAgIGF3YWl0IGZhYXN0TW9kdWxlLmZ1bmN0aW9ucy5jb25zb2xlV2FybihcIlJlbW90ZSBjb25zb2xlLndhcm4gb3V0cHV0XCIpO1xuICAgICAgICBhd2FpdCBmYWFzdE1vZHVsZS5mdW5jdGlvbnMuY29uc29sZUVycm9yKFwiUmVtb3RlIGNvbnNvbGUuZXJyb3Igb3V0cHV0XCIpO1xuICAgICAgICBhd2FpdCBzbGVlcCgxMDAwKTtcbiAgICAgICAgYXdhaXQgZmFhc3RNb2R1bGUuY2xlYW51cCh7IGRlbGV0ZVJlc291cmNlczogZmFsc2UgfSk7XG4gICAgICAgIGNvbnN0IG1lc3NhZ2VzID0gYXdhaXQgcmVhZEZpcnN0TG9nZmlsZShmYWFzdE1vZHVsZS5sb2dVcmwoKSk7XG4gICAgICAgIHQudHJ1dGh5KG1lc3NhZ2VzLmZpbmQocyA9PiBzID09PSBcIlskcGlkXTogUmVtb3RlIGNvbnNvbGUubG9nIG91dHB1dFwiKSk7XG4gICAgICAgIHQudHJ1dGh5KG1lc3NhZ2VzLmZpbmQocyA9PiBzID09PSBcIlskcGlkXTogUmVtb3RlIGNvbnNvbGUud2FybiBvdXRwdXRcIikpO1xuICAgICAgICB0LnRydXRoeShtZXNzYWdlcy5maW5kKHMgPT4gcyA9PT0gXCJbJHBpZF06IFJlbW90ZSBjb25zb2xlLmVycm9yIG91dHB1dFwiKSk7XG4gICAgfSBmaW5hbGx5IHtcbiAgICAgICAgYXdhaXQgZmFhc3RNb2R1bGUuY2xlYW51cCh7IGRlbGV0ZVJlc291cmNlczogZmFsc2UgfSk7XG4gICAgfVxufSk7XG5cbnRlc3QoXCJsb2NhbCBwcm92aWRlciBsb2cgZmlsZXMgc2hvdWxkIGJlIGFwcGVuZGVkLCBub3QgdHJ1bmNhdGVkLCBhZnRlciBjaGlsZCBwcm9jZXNzIGNyYXNoXCIsIGFzeW5jIHQgPT4ge1xuICAgIGNvbnN0IGZhYXN0TW9kdWxlID0gYXdhaXQgZmFhc3RMb2NhbChmdW5jcywge1xuICAgICAgICBjaGlsZFByb2Nlc3M6IHRydWUsXG4gICAgICAgIGNvbmN1cnJlbmN5OiAxLFxuICAgICAgICBtYXhSZXRyaWVzOiAxLFxuICAgICAgICBnYzogXCJvZmZcIlxuICAgIH0pO1xuICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IGZhYXN0TW9kdWxlLmZ1bmN0aW9ucy5jb25zb2xlTG9nKFwib3V0cHV0IDFcIik7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBhd2FpdCBmYWFzdE1vZHVsZS5mdW5jdGlvbnMucHJvY2Vzc0V4aXQoKTtcbiAgICAgICAgfSBjYXRjaCAoZXJyOiBhbnkpIHt9XG4gICAgICAgIGF3YWl0IGZhYXN0TW9kdWxlLmZ1bmN0aW9ucy5jb25zb2xlV2FybihcIm91dHB1dCAyXCIpO1xuXG4gICAgICAgIC8vIFdhaXQgZm9yIGZsdXNoXG4gICAgICAgIGF3YWl0IHNsZWVwKDUwMCk7XG4gICAgICAgIGNvbnN0IG1lc3NhZ2VzID0gYXdhaXQgcmVhZEZpcnN0TG9nZmlsZShmYWFzdE1vZHVsZS5sb2dVcmwoKSk7XG5cbiAgICAgICAgdC50cnV0aHkobWVzc2FnZXMuZmluZChzID0+IHMgPT09IFwiWyRwaWRdOiBvdXRwdXQgMVwiKSk7XG4gICAgICAgIHQudHJ1dGh5KG1lc3NhZ2VzLmZpbmQocyA9PiBzID09PSBcIlskcGlkXTogb3V0cHV0IDJcIikpO1xuICAgIH0gZmluYWxseSB7XG4gICAgICAgIGF3YWl0IGZhYXN0TW9kdWxlLmNsZWFudXAoeyBkZWxldGVSZXNvdXJjZXM6IGZhbHNlIH0pO1xuICAgIH1cbn0pO1xuXG50ZXN0KFwibG9jYWwgcHJvdmlkZXIgY2hpbGQgcHJvY2VzcyBleGNlcHRpb25zIHNob3VsZCByZXN1bHQgaW4gZXJyb3JzIHdpdGggbG9nVXJsXCIsIGFzeW5jIHQgPT4ge1xuICAgIGNvbnN0IGZhYXN0TW9kdWxlID0gYXdhaXQgZmFhc3RMb2NhbChmdW5jcywge1xuICAgICAgICBjaGlsZFByb2Nlc3M6IHRydWUsXG4gICAgICAgIGNvbmN1cnJlbmN5OiAxLFxuICAgICAgICBtYXhSZXRyaWVzOiAxLFxuICAgICAgICBnYzogXCJvZmZcIlxuICAgIH0pO1xuICAgIHQucGxhbigxKTtcbiAgICB0cnkge1xuICAgICAgICBhd2FpdCBmYWFzdE1vZHVsZS5mdW5jdGlvbnMuZXJyb3IoXCJzeW50aGV0aWMgZXJyb3JcIik7XG4gICAgfSBjYXRjaCAoZXJyOiBhbnkpIHtcbiAgICAgICAgY29uc3QgaW5mbyA9IEZhYXN0RXJyb3IuaW5mbyhlcnIpO1xuICAgICAgICB0LnRydWUoXG4gICAgICAgICAgICB0eXBlb2YgaW5mby5sb2dVcmwgPT09IFwic3RyaW5nXCIgJiYgaW5mby5sb2dVcmwuc3RhcnRzV2l0aChcIiBmaWxlOi8vL1wiKSxcbiAgICAgICAgICAgIGluc3BlY3QoZXJyKVxuICAgICAgICApO1xuICAgIH0gZmluYWxseSB7XG4gICAgICAgIGF3YWl0IGZhYXN0TW9kdWxlLmNsZWFudXAoKTtcbiAgICB9XG59KTtcblxudGVzdChcImxvY2FsIHByb3ZpZGVyIGNoaWxkIHByb2Nlc3MgY3Jhc2hlcyBzaG91bGQgcmVzdWx0IGluIGVycm9ycyB3aXRoIGxvZ1VybFwiLCBhc3luYyB0ID0+IHtcbiAgICBjb25zdCBmYWFzdE1vZHVsZSA9IGF3YWl0IGZhYXN0TG9jYWwoZnVuY3MsIHtcbiAgICAgICAgY2hpbGRQcm9jZXNzOiB0cnVlLFxuICAgICAgICBjb25jdXJyZW5jeTogMSxcbiAgICAgICAgbWF4UmV0cmllczogMSxcbiAgICAgICAgZ2M6IFwib2ZmXCJcbiAgICB9KTtcbiAgICB0LnBsYW4oMSk7XG4gICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgZmFhc3RNb2R1bGUuZnVuY3Rpb25zLnByb2Nlc3NFeGl0KC0xKTtcbiAgICB9IGNhdGNoIChlcnI6IGFueSkge1xuICAgICAgICBjb25zdCBpbmZvID0gRmFhc3RFcnJvci5pbmZvKGVycik7XG4gICAgICAgIHQudHJ1ZShcbiAgICAgICAgICAgIHR5cGVvZiBpbmZvLmxvZ1VybCA9PT0gXCJzdHJpbmdcIiAmJiBpbmZvLmxvZ1VybC5zdGFydHNXaXRoKFwiIGZpbGU6Ly8vXCIpLFxuICAgICAgICAgICAgaW5zcGVjdChlcnIpXG4gICAgICAgICk7XG4gICAgfSBmaW5hbGx5IHtcbiAgICAgICAgYXdhaXQgZmFhc3RNb2R1bGUuY2xlYW51cCgpO1xuICAgIH1cbn0pO1xuXG50ZXN0KFwibG9jYWwgcHJvdmlkZXIgY29uY3VycmVudCBleGVjdXRpb25zIHdpdGggY2hpbGQgcHJvY2Vzc2VzXCIsIGFzeW5jIHQgPT4ge1xuICAgIGF3YWl0IHRlc3RDb25jdXJyZW5jeSh0LCB7XG4gICAgICAgIG9wdGlvbnM6IHtcbiAgICAgICAgICAgIGNoaWxkUHJvY2VzczogdHJ1ZVxuICAgICAgICB9LFxuICAgICAgICBtYXhDb25jdXJyZW5jeTogNSxcbiAgICAgICAgZXhwZWN0ZWRDb25jdXJyZW5jeTogNVxuICAgIH0pO1xufSk7XG5cbnRlc3QoXCJsb2NhbCBwcm92aWRlciBubyBjb25jdXJyZW5jeSBmb3IgY3B1IGJvdW5kIHdvcmsgd2l0aG91dCBjaGlsZCBwcm9jZXNzZXNcIiwgYXN5bmMgdCA9PiB7XG4gICAgYXdhaXQgdGVzdENvbmN1cnJlbmN5KHQsIHtcbiAgICAgICAgb3B0aW9uczoge1xuICAgICAgICAgICAgY2hpbGRQcm9jZXNzOiBmYWxzZVxuICAgICAgICB9LFxuICAgICAgICBtYXhDb25jdXJyZW5jeTogNSxcbiAgICAgICAgZXhwZWN0ZWRDb25jdXJyZW5jeTogMVxuICAgIH0pO1xufSk7XG5cbnRlc3QoXCJsb2NhbCBwcm92aWRlciBjbGVhbnVwIHdhaXRzIGZvciBhbGwgY2hpbGQgcHJvY2Vzc2VzIHRvIGV4aXRcIiwgYXN5bmMgdCA9PiB7XG4gICAgY29uc3QgZmFhc3RNb2R1bGUgPSBhd2FpdCBmYWFzdExvY2FsKGZ1bmNzLCB7XG4gICAgICAgIGNoaWxkUHJvY2VzczogdHJ1ZSxcbiAgICAgICAgZ2M6IFwib2ZmXCJcbiAgICB9KTtcbiAgICBmYWFzdE1vZHVsZS5mdW5jdGlvbnMuc3Bpbig1MDAwKS5jYXRjaChfID0+IHt9KTtcbiAgICB3aGlsZSAodHJ1ZSkge1xuICAgICAgICBhd2FpdCBzbGVlcCgxMDApO1xuICAgICAgICBpZiAoZmFhc3RNb2R1bGUuc3RhdGUuZXhlY3V0b3JzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgfVxuICAgIHQuaXMoZmFhc3RNb2R1bGUuc3RhdGUuZXhlY3V0b3JzLmxlbmd0aCwgMSwgXCJleGVjdXRvciBpcyBub3QgcnVubmluZ1wiKTtcbiAgICBhd2FpdCBmYWFzdE1vZHVsZS5jbGVhbnVwKHsgZ2NUaW1lb3V0OiA2MCB9KTtcbiAgICB0LmlzKGZhYXN0TW9kdWxlLnN0YXRlLmV4ZWN1dG9ycy5sZW5ndGgsIDAsIFwiZXhlY3V0b3JzIGFyZSBydW5uaW5nIGFmdGVyIGNsZWFudXBcIik7XG59KTtcblxudGVzdChcImxvY2FsIHVucmVzb2x2ZWQgbW9kdWxlXCIsIGFzeW5jIHQgPT4ge1xuICAgIHQucGxhbigxKTtcbiAgICB0cnkge1xuICAgICAgICBhd2FpdCBmYWFzdExvY2FsKHt9KTtcbiAgICB9IGNhdGNoIChlcnI6IGFueSkge1xuICAgICAgICB0LnJlZ2V4KGVyci5tZXNzYWdlLCAvQ291bGQgbm90IGZpbmQgZmlsZS8pO1xuICAgIH1cbn0pO1xuXG50ZXN0KFwibG9jYWwgaXNzdWUgIzM3XCIsIGFzeW5jIHQgPT4ge1xuICAgIC8vIFByZXZpb3VzbHkgdGhpcyBjb2RlIGNhdXNlZCBhbiBleGNlcHRpb24gYWJvdXQgbW9kdWxlIHdyYXBwZXIgbm90IGJlaW5nXG4gICAgLy8gcmUtZW50cmFudC4gVGhlIHByb2JsZW0gd2FzIGEgcmFjZSBjb25kaXRpb24gYmV0d2VlbiB3cmFwcGVyIHNlbGVjdGlvblxuICAgIC8vIGFuZCBleGVjdXRpb24gaW4gbG9jYWwgcHJvdmlkZXIuIFNvbHZlZCBieSBtYWtpbmcgd3JhcHBlciBzZWxlY3RvciBhXG4gICAgLy8gcmVndWxhciBmdW5jdGlvbiBpbnN0ZWFkIG9mIGFuIGFzeW5jIGZ1bmN0aW9uLlxuICAgIGNvbnN0IG0gPSBhd2FpdCBmYWFzdExvY2FsKGZ1bmNzKTtcbiAgICB0cnkge1xuICAgICAgICBjb25zdCB7IGlkZW50aXR5U3RyaW5nOiBpZGVudGl0eSB9ID0gbS5mdW5jdGlvbnM7XG4gICAgICAgIGF3YWl0IGlkZW50aXR5KFwiYVwiKTtcbiAgICAgICAgY29uc3QgYiA9IGlkZW50aXR5KFwiYlwiKTtcbiAgICAgICAgY29uc3QgYyA9IGlkZW50aXR5KFwiY1wiKTtcbiAgICAgICAgYXdhaXQgYjtcbiAgICAgICAgYXdhaXQgYztcbiAgICAgICAgLy8gVGVzdCBzdWNjZWVkcyBpZiBubyBleGNlcHRpb25zIGFyZSB0aHJvd24uXG4gICAgICAgIHQudHJ1ZSh0cnVlKTtcbiAgICB9IGZpbmFsbHkge1xuICAgICAgICBhd2FpdCBtLmNsZWFudXAoKTtcbiAgICB9XG59KTtcbiJdfQ==
\No newline at end of file