UNPKG

faastjs

Version:

Serverless batch computing made simple.

236 lines 30.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const tslib_1 = require("tslib"); const ava_1 = tslib_1.__importDefault(require("ava")); const fs_extra_1 = require("fs-extra"); const url_1 = require("url"); const util_1 = require("util"); const index_1 = require("../index"); const funcs = tslib_1.__importStar(require("./fixtures/functions")); const util_2 = require("./fixtures/util"); const error_1 = require("../src/error"); async function testCleanup(t, options) { const m = await (0, index_1.faastLocal)(funcs, { gc: "off", ...options }); let done = 0; m.functions .hello("there") .then(_ => done++) .catch(_ => { }); m.functions .sleep(1000) .then(_ => done++) .catch(_ => { }); await m.cleanup(); t.is(done, 0); } async function testOrder(t, options) { const faastModule = await (0, index_1.faastLocal)(funcs, { gc: "off", ...options }); t.plan(2); const a = faastModule.functions.emptyReject(); const b = faastModule.functions.sleep(0); t.is(await b, undefined); try { await a; } catch (err) { t.is(err, undefined); } finally { await faastModule.cleanup(); } } async function testConcurrency(t, { options, maxConcurrency, expectedConcurrency }) { const faastModule = await (0, index_1.faastLocal)(funcs, { ...options, gc: "off", concurrency: maxConcurrency }); try { const N = maxConcurrency * 2; const promises = []; for (let i = 0; i < N; i++) { promises.push(faastModule.functions.spin(2000)); } const timings = await Promise.all(promises); t.is((0, util_2.measureConcurrency)(timings), expectedConcurrency); } finally { await faastModule.cleanup(); } } (0, ava_1.default)("local provider cleanup stops executions", testCleanup, {}); (0, ava_1.default)("local provider cleanup stops executions with child process", testCleanup, { childProcess: true }); const orderConfigs = [ { childProcess: false, concurrency: 1, maxRetries: 0 }, { childProcess: true, concurrency: 1, maxRetries: 0 }, { childProcess: false, concurrency: 2, maxRetries: 0 }, { childProcess: true, concurrency: 2, maxRetries: 0 }, { childProcess: false, concurrency: 2, maxRetries: 2 }, { childProcess: true, concurrency: 2, maxRetries: 2 } ]; for (const config of orderConfigs) { (0, ava_1.default)(`out of order await (async catch) with ${(0, util_1.inspect)(config)}`, testOrder, config); } async function readFirstLogfile(logDirectoryUrl) { const url = new url_1.URL(logDirectoryUrl); const buf = await (0, fs_extra_1.readFile)(url.pathname + "/0.log"); return buf .toString() .split("\n") .map(m => m.replace(/^\[(\d+)\]/, "[$pid]")); } (0, ava_1.default)("local provider console.log, console.warn, and console.error with child process", async (t) => { const faastModule = await (0, index_1.faastLocal)(funcs, { childProcess: true, concurrency: 1, gc: "off" }); try { await faastModule.functions.consoleLog("Remote console.log output"); await faastModule.functions.consoleWarn("Remote console.warn output"); await faastModule.functions.consoleError("Remote console.error output"); await (0, util_2.sleep)(1000); await faastModule.cleanup({ deleteResources: false }); const messages = await readFirstLogfile(faastModule.logUrl()); t.truthy(messages.find(s => s === "[$pid]: Remote console.log output")); t.truthy(messages.find(s => s === "[$pid]: Remote console.warn output")); t.truthy(messages.find(s => s === "[$pid]: Remote console.error output")); } finally { await faastModule.cleanup({ deleteResources: false }); } }); (0, ava_1.default)("local provider log files should be appended, not truncated, after child process crash", async (t) => { const faastModule = await (0, index_1.faastLocal)(funcs, { childProcess: true, concurrency: 1, maxRetries: 1, gc: "off" }); try { await faastModule.functions.consoleLog("output 1"); try { await faastModule.functions.processExit(); } catch (err) { } await faastModule.functions.consoleWarn("output 2"); // Wait for flush await (0, util_2.sleep)(500); const messages = await readFirstLogfile(faastModule.logUrl()); t.truthy(messages.find(s => s === "[$pid]: output 1")); t.truthy(messages.find(s => s === "[$pid]: output 2")); } finally { await faastModule.cleanup({ deleteResources: false }); } }); (0, ava_1.default)("local provider child process exceptions should result in errors with logUrl", async (t) => { const faastModule = await (0, index_1.faastLocal)(funcs, { childProcess: true, concurrency: 1, maxRetries: 1, gc: "off" }); t.plan(1); try { await faastModule.functions.error("synthetic error"); } catch (err) { const info = error_1.FaastError.info(err); t.true(typeof info.logUrl === "string" && info.logUrl.startsWith(" file:///"), (0, util_1.inspect)(err)); } finally { await faastModule.cleanup(); } }); (0, ava_1.default)("local provider child process crashes should result in errors with logUrl", async (t) => { const faastModule = await (0, index_1.faastLocal)(funcs, { childProcess: true, concurrency: 1, maxRetries: 1, gc: "off" }); t.plan(1); try { await faastModule.functions.processExit(-1); } catch (err) { const info = error_1.FaastError.info(err); t.true(typeof info.logUrl === "string" && info.logUrl.startsWith(" file:///"), (0, util_1.inspect)(err)); } finally { await faastModule.cleanup(); } }); (0, ava_1.default)("local provider concurrent executions with child processes", async (t) => { await testConcurrency(t, { options: { childProcess: true }, maxConcurrency: 5, expectedConcurrency: 5 }); }); (0, ava_1.default)("local provider no concurrency for cpu bound work without child processes", async (t) => { await testConcurrency(t, { options: { childProcess: false }, maxConcurrency: 5, expectedConcurrency: 1 }); }); (0, ava_1.default)("local provider cleanup waits for all child processes to exit", async (t) => { const faastModule = await (0, index_1.faastLocal)(funcs, { childProcess: true, gc: "off" }); faastModule.functions.spin(5000).catch(_ => { }); while (true) { await (0, util_2.sleep)(100); if (faastModule.state.executors.length > 0) { break; } } t.is(faastModule.state.executors.length, 1, "executor is not running"); await faastModule.cleanup({ gcTimeout: 60 }); t.is(faastModule.state.executors.length, 0, "executors are running after cleanup"); }); (0, ava_1.default)("local unresolved module", async (t) => { t.plan(1); try { await (0, index_1.faastLocal)({}); } catch (err) { t.regex(err.message, /Could not find file/); } }); (0, ava_1.default)("local issue #37", async (t) => { // Previously this code caused an exception about module wrapper not being // re-entrant. The problem was a race condition between wrapper selection // and execution in local provider. Solved by making wrapper selector a // regular function instead of an async function. const m = await (0, index_1.faastLocal)(funcs); try { const { identityString: identity } = m.functions; await identity("a"); const b = identity("b"); const c = identity("c"); await b; await c; // Test succeeds if no exceptions are thrown. t.true(true); } finally { await m.cleanup(); } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidW5pdC1sb2NhbC50ZXN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdGVzdC91bml0LWxvY2FsLnRlc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsc0RBQTZDO0FBQzdDLHVDQUFvQztBQUNwQyw2QkFBMEI7QUFDMUIsK0JBQStCO0FBQy9CLG9DQUFvRDtBQUNwRCxvRUFBOEM7QUFDOUMsMENBQTREO0FBQzVELHdDQUEwQztBQUUxQyxLQUFLLFVBQVUsV0FBVyxDQUFDLENBQW1CLEVBQUUsT0FBcUI7SUFDakUsTUFBTSxDQUFDLEdBQUcsTUFBTSxJQUFBLGtCQUFVLEVBQUMsS0FBSyxFQUFFO1FBQzlCLEVBQUUsRUFBRSxLQUFLO1FBQ1QsR0FBRyxPQUFPO0tBQ2IsQ0FBQyxDQUFDO0lBQ0gsSUFBSSxJQUFJLEdBQUcsQ0FBQyxDQUFDO0lBRWIsQ0FBQyxDQUFDLFNBQVM7U0FDTixLQUFLLENBQUMsT0FBTyxDQUFDO1NBQ2QsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsSUFBSSxFQUFFLENBQUM7U0FDakIsS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUFFLEdBQUUsQ0FBQyxDQUFDLENBQUM7SUFFcEIsQ0FBQyxDQUFDLFNBQVM7U0FDTixLQUFLLENBQUMsSUFBSSxDQUFDO1NBQ1gsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsSUFBSSxFQUFFLENBQUM7U0FDakIsS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUFFLEdBQUUsQ0FBQyxDQUFDLENBQUM7SUFFcEIsTUFBTSxDQUFDLENBQUMsT0FBTyxFQUFFLENBQUM7SUFDbEIsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUM7QUFDbEIsQ0FBQztBQUVELEtBQUssVUFBVSxTQUFTLENBQUMsQ0FBbUIsRUFBRSxPQUFxQjtJQUMvRCxNQUFNLFdBQVcsR0FBRyxNQUFNLElBQUEsa0JBQVUsRUFBQyxLQUFLLEVBQUU7UUFDeEMsRUFB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dEIsQ0FBQztBQUNMLENBQUMsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHRlc3QsIHsgRXhlY3V0aW9uQ29udGV4dCB9IGZyb20gXCJhdmFcIjtcbmltcG9ydCB7IHJlYWRGaWxlIH0gZnJvbSBcImZzLWV4dHJhXCI7XG5pbXBvcnQgeyBVUkwgfSBmcm9tIFwidXJsXCI7XG5pbXBvcnQgeyBpbnNwZWN0IH0gZnJvbSBcInV0aWxcIjtcbmltcG9ydCB7IGZhYXN0TG9jYWwsIExvY2FsT3B0aW9ucyB9IGZyb20gXCIuLi9pbmRleFwiO1xuaW1wb3J0ICogYXMgZnVuY3MgZnJvbSBcIi4vZml4dHVyZXMvZnVuY3Rpb25zXCI7XG5pbXBvcnQgeyBtZWFzdXJlQ29uY3VycmVuY3ksIHNsZWVwIH0gZnJvbSBcIi4vZml4dHVyZXMvdXRpbFwiO1xuaW1wb3J0IHsgRmFhc3RFcnJvciB9IGZyb20gXCIuLi9zcmMvZXJyb3JcIjtcblxuYXN5bmMgZnVuY3Rpb24gdGVzdENsZWFudXAodDogRXhlY3V0aW9uQ29udGV4dCwgb3B0aW9uczogTG9jYWxPcHRpb25zKSB7XG4gICAgY29uc3QgbSA9IGF3YWl0IGZhYXN0TG9jYWwoZnVuY3MsIHtcbiAgICAgICAgZ2M6IFwib2ZmXCIsXG4gICAgICAgIC4uLm9wdGlvbnNcbiAgICB9KTtcbiAgICBsZXQgZG9uZSA9IDA7XG5cbiAgICBtLmZ1bmN0aW9uc1xuICAgICAgICAuaGVsbG8oXCJ0aGVyZVwiKVxuICAgICAgICAudGhlbihfID0+IGRvbmUrKylcbiAgICAgICAgLmNhdGNoKF8gPT4ge30pO1xuXG4gICAgbS5mdW5jdGlvbnNcbiAgICAgICAgLnNsZWVwKDEwMDApXG4gICAgICAgIC50aGVuKF8gPT4gZG9uZSsrKVxuICAgICAgICAuY2F0Y2goXyA9PiB7fSk7XG5cbiAgICBhd2FpdCBtLmNsZWFudXAoKTtcbiAgICB0LmlzKGRvbmUsIDApO1xufVxuXG5hc3luYyBmdW5jdGlvbiB0ZXN0T3JkZXIodDogRXhlY3V0aW9uQ29udGV4dCwgb3B0aW9uczogTG9jYWxPcHRpb25zKSB7XG4gICAgY29uc3QgZmFhc3RNb2R1bGUgPSBhd2FpdCBmYWFzdExvY2FsKGZ1bmNzLCB7XG4gICAgICAgIGdjOiBcIm9mZlwiLFxuICAgICAgICAuLi5vcHRpb25zXG4gICAgfSk7XG4gICAgdC5wbGFuKDIpO1xuXG4gICAgY29uc3QgYSA9IGZhYXN0TW9kdWxlLmZ1bmN0aW9ucy5lbXB0eVJlamVjdCgpO1xuICAgIGNvbnN0IGIgPSBmYWFzdE1vZHVsZS5mdW5jdGlvbnMuc2xlZXAoMCk7XG4gICAgdC5pcyhhd2FpdCBiLCB1bmRlZmluZWQpO1xuICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IGE7XG4gICAgfSBjYXRjaCAoZXJyOiBhbnkpIHtcbiAgICAgICAgdC5pcyhlcnIsIHVuZGVmaW5lZCk7XG4gICAgfSBmaW5hbGx5IHtcbiAgICAgICAgYXdhaXQgZmFhc3RNb2R1bGUuY2xlYW51cCgpO1xuICAgIH1cbn1cblxuYXN5bmMgZnVuY3Rpb24gdGVzdENvbmN1cnJlbmN5KFxuICAgIHQ6IEV4ZWN1dGlvbkNvbnRleHQsXG4gICAge1xuICAgICAgICBvcHRpb25zLFxuICAgICAgICBtYXhDb25jdXJyZW5jeSxcbiAgICAgICAgZXhwZWN0ZWRDb25jdXJyZW5jeVxuICAgIH06IHtcbiAgICAgICAgb3B0aW9uczogTG9jYWxPcHRpb25zO1xuICAgICAgICBtYXhDb25jdXJyZW5jeTogbnVtYmVyO1xuICAgICAgICBleHBlY3RlZENvbmN1cnJlbmN5OiBudW1iZXI7XG4gICAgfVxuKSB7XG4gICAgY29uc3QgZmFhc3RNb2R1bGUgPSBhd2FpdCBmYWFzdExvY2FsKGZ1bmNzLCB7XG4gICAgICAgIC4uLm9wdGlvbnMsXG4gICAgICAgIGdjOiBcIm9mZlwiLFxuICAgICAgICBjb25jdXJyZW5jeTogbWF4Q29uY3VycmVuY3lcbiAgICB9KTtcblxuICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IE4gPSBtYXhDb25jdXJyZW5jeSAqIDI7XG4gICAgICAgIGNvbnN0IHByb21pc2VzID0gW107XG4gICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgTjsgaSsrKSB7XG4gICAgICAgICAgICBwcm9taXNlcy5wdXNoKGZhYXN0TW9kdWxlLmZ1bmN0aW9ucy5zcGluKDIwMDApKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IHRpbWluZ3MgPSBhd2FpdCBQcm9taXNlLmFsbChwcm9taXNlcyk7XG4gICAgICAgIHQuaXMobWVhc3VyZUNvbmN1cnJlbmN5KHRpbWluZ3MpLCBleHBlY3RlZENvbmN1cnJlbmN5KTtcbiAgICB9IGZpbmFsbHkge1xuICAgICAgICBhd2FpdCBmYWFzdE1vZHVsZS5jbGVhbnVwKCk7XG4gICAgfVxufVxuXG50ZXN0KFwibG9jYWwgcHJvdmlkZXIgY2xlYW51cCBzdG9wcyBleGVjdXRpb25zXCIsIHRlc3RDbGVhbnVwLCB7fSk7XG50ZXN0KFwibG9jYWwgcHJvdmlkZXIgY2xlYW51cCBzdG9wcyBleGVjdXRpb25zIHdpdGggY2hpbGQgcHJvY2Vzc1wiLCB0ZXN0Q2xlYW51cCwge1xuICAgIGNoaWxkUHJvY2VzczogdHJ1ZVxufSk7XG5cbmNvbnN0IG9yZGVyQ29uZmlncyA9IFtcbiAgICB7IGNoaWxkUHJvY2VzczogZmFsc2UsIGNvbmN1cnJlbmN5OiAxLCBtYXhSZXRyaWVzOiAwIH0sXG4gICAgeyBjaGlsZFByb2Nlc3M6IHRydWUsIGNvbmN1cnJlbmN5OiAxLCBtYXhSZXRyaWVzOiAwIH0sXG4gICAgeyBjaGlsZFByb2Nlc3M6IGZhbHNlLCBjb25jdXJyZW5jeTogMiwgbWF4UmV0cmllczogMCB9LFxuICAgIHsgY2hpbGRQcm9jZXNzOiB0cnVlLCBjb25jdXJyZW5jeTogMiwgbWF4UmV0cmllczogMCB9LFxuICAgIHsgY2hpbGRQcm9jZXNzOiBmYWxzZSwgY29uY3VycmVuY3k6IDIsIG1heFJldHJpZXM6IDIgfSxcbiAgICB7IGNoaWxkUHJvY2VzczogdHJ1ZSwgY29uY3VycmVuY3k6IDIsIG1heFJldHJpZXM6IDIgfVxuXTtcblxuZm9yIChjb25zdCBjb25maWcgb2Ygb3JkZXJDb25maWdzKSB7XG4gICAgdGVzdChgb3V0IG9mIG9yZGVyIGF3YWl0IChhc3luYyBjYXRjaCkgd2l0aCAke2luc3BlY3QoY29uZmlnKX1gLCB0ZXN0T3JkZXIsIGNvbmZpZyk7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIHJlYWRGaXJzdExvZ2ZpbGUobG9nRGlyZWN0b3J5VXJsOiBzdHJpbmcpIHtcbiAgICBjb25zdCB1cmwgPSBuZXcgVVJMKGxvZ0RpcmVjdG9yeVVybCk7XG4gICAgY29uc3QgYnVmID0gYXdhaXQgcmVhZEZpbGUodXJsLnBhdGhuYW1lICsgXCIvMC5sb2dcIik7XG4gICAgcmV0dXJuIGJ1ZlxuICAgICAgICAudG9TdHJpbmcoKVxuICAgICAgICAuc3BsaXQoXCJcXG5cIilcbiAgICAgICAgLm1hcChtID0+IG0ucmVwbGFjZSgvXlxcWyhcXGQrKVxcXS8sIFwiWyRwaWRdXCIpKTtcbn1cblxudGVzdChcImxvY2FsIHByb3ZpZGVyIGNvbnNvbGUubG9nLCBjb25zb2xlLndhcm4sIGFuZCBjb25zb2xlLmVycm9yIHdpdGggY2hpbGQgcHJvY2Vzc1wiLCBhc3luYyB0ID0+IHtcbiAgICBjb25zdCBmYWFzdE1vZHVsZSA9IGF3YWl0IGZhYXN0TG9jYWwoZnVuY3MsIHtcbiAgICAgICAgY2hpbGRQcm9jZXNzOiB0cnVlLFxuICAgICAgICBjb25jdXJyZW5jeTogMSxcbiAgICAgICAgZ2M6IFwib2ZmXCJcbiAgICB9KTtcbiAgICB0cnkge1xuICAgICAgICBhd2FpdCBmYWFzdE1vZHVsZS5mdW5jdGlvbnMuY29uc29sZUxvZyhcIlJlbW90ZSBjb25zb2xlLmxvZyBvdXRwdXRcIik7XG4gICAgICAgIGF3YWl0IGZhYXN0TW9kdWxlLmZ1bmN0aW9ucy5jb25zb2xlV2FybihcIlJlbW90ZSBjb25zb2xlLndhcm4gb3V0cHV0XCIpO1xuICAgICAgICBhd2FpdCBmYWFzdE1vZHVsZS5mdW5jdGlvbnMuY29uc29sZUVycm9yKFwiUmVtb3RlIGNvbnNvbGUuZXJyb3Igb3V0cHV0XCIpO1xuICAgICAgICBhd2FpdCBzbGVlcCgxMDAwKTtcbiAgICAgICAgYXdhaXQgZmFhc3RNb2R1bGUuY2xlYW51cCh7IGRlbGV0ZVJlc291cmNlczogZmFsc2UgfSk7XG4gICAgICAgIGNvbnN0IG1lc3NhZ2VzID0gYXdhaXQgcmVhZEZpcnN0TG9nZmlsZShmYWFzdE1vZHVsZS5sb2dVcmwoKSk7XG4gICAgICAgIHQudHJ1dGh5KG1lc3NhZ2VzLmZpbmQocyA9PiBzID09PSBcIlskcGlkXTogUmVtb3RlIGNvbnNvbGUubG9nIG91dHB1dFwiKSk7XG4gICAgICAgIHQudHJ1dGh5KG1lc3NhZ2VzLmZpbmQocyA9PiBzID09PSBcIlskcGlkXTogUmVtb3RlIGNvbnNvbGUud2FybiBvdXRwdXRcIikpO1xuICAgICAgICB0LnRydXRoeShtZXNzYWdlcy5maW5kKHMgPT4gcyA9PT0gXCJbJHBpZF06IFJlbW90ZSBjb25zb2xlLmVycm9yIG91dHB1dFwiKSk7XG4gICAgfSBmaW5hbGx5IHtcbiAgICAgICAgYXdhaXQgZmFhc3RNb2R1bGUuY2xlYW51cCh7IGRlbGV0ZVJlc291cmNlczogZmFsc2UgfSk7XG4gICAgfVxufSk7XG5cbnRlc3QoXCJsb2NhbCBwcm92aWRlciBsb2cgZmlsZXMgc2hvdWxkIGJlIGFwcGVuZGVkLCBub3QgdHJ1bmNhdGVkLCBhZnRlciBjaGlsZCBwcm9jZXNzIGNyYXNoXCIsIGFzeW5jIHQgPT4ge1xuICAgIGNvbnN0IGZhYXN0TW9kdWxlID0gYXdhaXQgZmFhc3RMb2NhbChmdW5jcywge1xuICAgICAgICBjaGlsZFByb2Nlc3M6IHRydWUsXG4gICAgICAgIGNvbmN1cnJlbmN5OiAxLFxuICAgICAgICBtYXhSZXRyaWVzOiAxLFxuICAgICAgICBnYzogXCJvZmZcIlxuICAgIH0pO1xuICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IGZhYXN0TW9kdWxlLmZ1bmN0aW9ucy5jb25zb2xlTG9nKFwib3V0cHV0IDFcIik7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBhd2FpdCBmYWFzdE1vZHVsZS5mdW5jdGlvbnMucHJvY2Vzc0V4aXQoKTtcbiAgICAgICAgfSBjYXRjaCAoZXJyOiBhbnkpIHt9XG4gICAgICAgIGF3YWl0IGZhYXN0TW9kdWxlLmZ1bmN0aW9ucy5jb25zb2xlV2FybihcIm91dHB1dCAyXCIpO1xuXG4gICAgICAgIC8vIFdhaXQgZm9yIGZsdXNoXG4gICAgICAgIGF3YWl0IHNsZWVwKDUwMCk7XG4gICAgICAgIGNvbnN0IG1lc3NhZ2VzID0gYXdhaXQgcmVhZEZpcnN0TG9nZmlsZShmYWFzdE1vZHVsZS5sb2dVcmwoKSk7XG5cbiAgICAgICAgdC50cnV0aHkobWVzc2FnZXMuZmluZChzID0+IHMgPT09IFwiWyRwaWRdOiBvdXRwdXQgMVwiKSk7XG4gICAgICAgIHQudHJ1dGh5KG1lc3NhZ2VzLmZpbmQocyA9PiBzID09PSBcIlskcGlkXTogb3V0cHV0IDJcIikpO1xuICAgIH0gZmluYWxseSB7XG4gICAgICAgIGF3YWl0IGZhYXN0TW9kdWxlLmNsZWFudXAoeyBkZWxldGVSZXNvdXJjZXM6IGZhbHNlIH0pO1xuICAgIH1cbn0pO1xuXG50ZXN0KFwibG9jYWwgcHJvdmlkZXIgY2hpbGQgcHJvY2VzcyBleGNlcHRpb25zIHNob3VsZCByZXN1bHQgaW4gZXJyb3JzIHdpdGggbG9nVXJsXCIsIGFzeW5jIHQgPT4ge1xuICAgIGNvbnN0IGZhYXN0TW9kdWxlID0gYXdhaXQgZmFhc3RMb2NhbChmdW5jcywge1xuICAgICAgICBjaGlsZFByb2Nlc3M6IHRydWUsXG4gICAgICAgIGNvbmN1cnJlbmN5OiAxLFxuICAgICAgICBtYXhSZXRyaWVzOiAxLFxuICAgICAgICBnYzogXCJvZmZcIlxuICAgIH0pO1xuICAgIHQucGxhbigxKTtcbiAgICB0cnkge1xuICAgICAgICBhd2FpdCBmYWFzdE1vZHVsZS5mdW5jdGlvbnMuZXJyb3IoXCJzeW50aGV0aWMgZXJyb3JcIik7XG4gICAgfSBjYXRjaCAoZXJyOiBhbnkpIHtcbiAgICAgICAgY29uc3QgaW5mbyA9IEZhYXN0RXJyb3IuaW5mbyhlcnIpO1xuICAgICAgICB0LnRydWUoXG4gICAgICAgICAgICB0eXBlb2YgaW5mby5sb2dVcmwgPT09IFwic3RyaW5nXCIgJiYgaW5mby5sb2dVcmwuc3RhcnRzV2l0aChcIiBmaWxlOi8vL1wiKSxcbiAgICAgICAgICAgIGluc3BlY3QoZXJyKVxuICAgICAgICApO1xuICAgIH0gZmluYWxseSB7XG4gICAgICAgIGF3YWl0IGZhYXN0TW9kdWxlLmNsZWFudXAoKTtcbiAgICB9XG59KTtcblxudGVzdChcImxvY2FsIHByb3ZpZGVyIGNoaWxkIHByb2Nlc3MgY3Jhc2hlcyBzaG91bGQgcmVzdWx0IGluIGVycm9ycyB3aXRoIGxvZ1VybFwiLCBhc3luYyB0ID0+IHtcbiAgICBjb25zdCBmYWFzdE1vZHVsZSA9IGF3YWl0IGZhYXN0TG9jYWwoZnVuY3MsIHtcbiAgICAgICAgY2hpbGRQcm9jZXNzOiB0cnVlLFxuICAgICAgICBjb25jdXJyZW5jeTogMSxcbiAgICAgICAgbWF4UmV0cmllczogMSxcbiAgICAgICAgZ2M6IFwib2ZmXCJcbiAgICB9KTtcbiAgICB0LnBsYW4oMSk7XG4gICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgZmFhc3RNb2R1bGUuZnVuY3Rpb25zLnByb2Nlc3NFeGl0KC0xKTtcbiAgICB9IGNhdGNoIChlcnI6IGFueSkge1xuICAgICAgICBjb25zdCBpbmZvID0gRmFhc3RFcnJvci5pbmZvKGVycik7XG4gICAgICAgIHQudHJ1ZShcbiAgICAgICAgICAgIHR5cGVvZiBpbmZvLmxvZ1VybCA9PT0gXCJzdHJpbmdcIiAmJiBpbmZvLmxvZ1VybC5zdGFydHNXaXRoKFwiIGZpbGU6Ly8vXCIpLFxuICAgICAgICAgICAgaW5zcGVjdChlcnIpXG4gICAgICAgICk7XG4gICAgfSBmaW5hbGx5IHtcbiAgICAgICAgYXdhaXQgZmFhc3RNb2R1bGUuY2xlYW51cCgpO1xuICAgIH1cbn0pO1xuXG50ZXN0KFwibG9jYWwgcHJvdmlkZXIgY29uY3VycmVudCBleGVjdXRpb25zIHdpdGggY2hpbGQgcHJvY2Vzc2VzXCIsIGFzeW5jIHQgPT4ge1xuICAgIGF3YWl0IHRlc3RDb25jdXJyZW5jeSh0LCB7XG4gICAgICAgIG9wdGlvbnM6IHtcbiAgICAgICAgICAgIGNoaWxkUHJvY2VzczogdHJ1ZVxuICAgICAgICB9LFxuICAgICAgICBtYXhDb25jdXJyZW5jeTogNSxcbiAgICAgICAgZXhwZWN0ZWRDb25jdXJyZW5jeTogNVxuICAgIH0pO1xufSk7XG5cbnRlc3QoXCJsb2NhbCBwcm92aWRlciBubyBjb25jdXJyZW5jeSBmb3IgY3B1IGJvdW5kIHdvcmsgd2l0aG91dCBjaGlsZCBwcm9jZXNzZXNcIiwgYXN5bmMgdCA9PiB7XG4gICAgYXdhaXQgdGVzdENvbmN1cnJlbmN5KHQsIHtcbiAgICAgICAgb3B0aW9uczoge1xuICAgICAgICAgICAgY2hpbGRQcm9jZXNzOiBmYWxzZVxuICAgICAgICB9LFxuICAgICAgICBtYXhDb25jdXJyZW5jeTogNSxcbiAgICAgICAgZXhwZWN0ZWRDb25jdXJyZW5jeTogMVxuICAgIH0pO1xufSk7XG5cbnRlc3QoXCJsb2NhbCBwcm92aWRlciBjbGVhbnVwIHdhaXRzIGZvciBhbGwgY2hpbGQgcHJvY2Vzc2VzIHRvIGV4aXRcIiwgYXN5bmMgdCA9PiB7XG4gICAgY29uc3QgZmFhc3RNb2R1bGUgPSBhd2FpdCBmYWFzdExvY2FsKGZ1bmNzLCB7XG4gICAgICAgIGNoaWxkUHJvY2VzczogdHJ1ZSxcbiAgICAgICAgZ2M6IFwib2ZmXCJcbiAgICB9KTtcbiAgICBmYWFzdE1vZHVsZS5mdW5jdGlvbnMuc3Bpbig1MDAwKS5jYXRjaChfID0+IHt9KTtcbiAgICB3aGlsZSAodHJ1ZSkge1xuICAgICAgICBhd2FpdCBzbGVlcCgxMDApO1xuICAgICAgICBpZiAoZmFhc3RNb2R1bGUuc3RhdGUuZXhlY3V0b3JzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgfVxuICAgIHQuaXMoZmFhc3RNb2R1bGUuc3RhdGUuZXhlY3V0b3JzLmxlbmd0aCwgMSwgXCJleGVjdXRvciBpcyBub3QgcnVubmluZ1wiKTtcbiAgICBhd2FpdCBmYWFzdE1vZHVsZS5jbGVhbnVwKHsgZ2NUaW1lb3V0OiA2MCB9KTtcbiAgICB0LmlzKGZhYXN0TW9kdWxlLnN0YXRlLmV4ZWN1dG9ycy5sZW5ndGgsIDAsIFwiZXhlY3V0b3JzIGFyZSBydW5uaW5nIGFmdGVyIGNsZWFudXBcIik7XG59KTtcblxudGVzdChcImxvY2FsIHVucmVzb2x2ZWQgbW9kdWxlXCIsIGFzeW5jIHQgPT4ge1xuICAgIHQucGxhbigxKTtcbiAgICB0cnkge1xuICAgICAgICBhd2FpdCBmYWFzdExvY2FsKHt9KTtcbiAgICB9IGNhdGNoIChlcnI6IGFueSkge1xuICAgICAgICB0LnJlZ2V4KGVyci5tZXNzYWdlLCAvQ291bGQgbm90IGZpbmQgZmlsZS8pO1xuICAgIH1cbn0pO1xuXG50ZXN0KFwibG9jYWwgaXNzdWUgIzM3XCIsIGFzeW5jIHQgPT4ge1xuICAgIC8vIFByZXZpb3VzbHkgdGhpcyBjb2RlIGNhdXNlZCBhbiBleGNlcHRpb24gYWJvdXQgbW9kdWxlIHdyYXBwZXIgbm90IGJlaW5nXG4gICAgLy8gcmUtZW50cmFudC4gVGhlIHByb2JsZW0gd2FzIGEgcmFjZSBjb25kaXRpb24gYmV0d2VlbiB3cmFwcGVyIHNlbGVjdGlvblxuICAgIC8vIGFuZCBleGVjdXRpb24gaW4gbG9jYWwgcHJvdmlkZXIuIFNvbHZlZCBieSBtYWtpbmcgd3JhcHBlciBzZWxlY3RvciBhXG4gICAgLy8gcmVndWxhciBmdW5jdGlvbiBpbnN0ZWFkIG9mIGFuIGFzeW5jIGZ1bmN0aW9uLlxuICAgIGNvbnN0IG0gPSBhd2FpdCBmYWFzdExvY2FsKGZ1bmNzKTtcbiAgICB0cnkge1xuICAgICAgICBjb25zdCB7IGlkZW50aXR5U3RyaW5nOiBpZGVudGl0eSB9ID0gbS5mdW5jdGlvbnM7XG4gICAgICAgIGF3YWl0IGlkZW50aXR5KFwiYVwiKTtcbiAgICAgICAgY29uc3QgYiA9IGlkZW50aXR5KFwiYlwiKTtcbiAgICAgICAgY29uc3QgYyA9IGlkZW50aXR5KFwiY1wiKTtcbiAgICAgICAgYXdhaXQgYjtcbiAgICAgICAgYXdhaXQgYztcbiAgICAgICAgLy8gVGVzdCBzdWNjZWVkcyBpZiBubyBleGNlcHRpb25zIGFyZSB0aHJvd24uXG4gICAgICAgIHQudHJ1ZSh0cnVlKTtcbiAgICB9IGZpbmFsbHkge1xuICAgICAgICBhd2FpdCBtLmNsZWFudXAoKTtcbiAgICB9XG59KTtcbiJdfQ==