UNPKG

@riddance/service

Version:

204 lines 28.5 kB
/* eslint-disable no-console */ import { createContext, } from '@riddance/host/context'; import { setMeta } from '@riddance/host/registry'; import { mkdir, readdir, readFile, writeFile } from 'node:fs/promises'; import { EOL } from 'node:os'; import { basename, extname, join, relative, sep } from 'node:path'; import { performance } from 'node:perf_hooks'; import { pathToFileURL } from 'node:url'; export function setup() { setupTestContext(); before(async () => { const { name, config } = await readConfig(); const dir = process.cwd(); const files = (await readdir('.')).filter(file => extname(file) === '.ts' && !file.endsWith('.d.ts')); for (const file of files) { const base = basename(file, '.ts'); setMeta(name, base, undefined, config); await import(pathToFileURL(join(dir, base + '.js')).toString()); } }); } async function readEnv() { try { const envText = await readFile('test/env.txt', 'utf-8'); return Object.fromEntries(envText .split('\n') .filter(l => l.length !== 0 && !l.startsWith('#')) .map(line => { const ix = line.indexOf('='); return [line.slice(0, ix).trim(), line.slice(ix + 1).trim()]; })); } catch (e) { if (e.code === 'ENOENT') { return {}; } throw e; } } async function readConfig() { const packageJson = JSON.parse(await readFile('package.json', 'utf-8')); return packageJson; } let testContext; function setupTestContext() { beforeEach('Clear logged entries', async () => { const env = await readEnv(); if (testContext) { throw new Error('Context exists.'); } testContext = new TestContext(env); }); afterEach('Check log', async function () { if (!testContext) { throw new Error('Test context lost.'); } const test = this.currentTest; if (test) { const title = test.fullTitle(); if (test.isFailed()) { await testContext.log.dumpLog(title); } if (testContext.log.failed) { if (!test.isFailed()) { await testContext.log.dumpLog(title); throw new Error(`"${title}" passed but subsequently failed because errors was logged during the test. Wrap the test code in allowErrorLogs if the error log entries are expected.`); } } } testContext = undefined; }); } export function createMockContext(client, config, meta) { const ctx = getTestContext(); return createContext(client, [ctx.log], { publishRate: 100, sendEvents: (topic, events) => { ctx.emitted.push(...events.map(e => ({ topic, type: e.meta.type, subject: e.meta.subject, data: e.json, messageId: e.meta.id, }))); return Promise.resolve(); }, }, { default: 15 }, new AbortController(), config, meta, ctx.env, () => ctx.now()); } export function getTestContext() { if (!testContext) { throw new Error('No test is running.'); } return testContext; } class MockLogger { #entries = []; #startTime = Math.round(performance.now() * 10_000); failOnErrorLogs = true; failed = false; getEntries() { return [...this.#entries]; } clear() { this.#entries = []; this.failOnErrorLogs = true; this.failed = false; } sendEntries(entries) { if (this.failOnErrorLogs && entries.some(e => e.level === 'error' || e.level === 'fatal')) { this.failed = true; } this.#entries.push(...entries); return undefined; } #msSinceStart(entry) { return (Math.round(entry.timestamp * 10_000) - this.#startTime) / 10_000; } async dumpLog(testTitle) { if (this.#entries.length !== 0) { const p = this.writeLog(); const errors = this.#entries.filter(e => e.level === 'fatal' || e.level === 'error'); if (errors.length !== 0) { console.error(testTitle + ' error log:'); errors.forEach(e => { console.error(`@${this.#msSinceStart(e)}ms ${levelString(e.level)} ${e.message}`); if (e.error) { console.error(e.error); } }); } const logFile = await p; if (logFile) { console.info(`Full log of "${testTitle}" saved to .${sep}${relative(process.env.PROJECT_DIRECTORY ?? process.cwd(), logFile)}`); } } } async writeLog() { try { const resultPath = join('test', 'results'); await mkdir(resultPath, { recursive: true }); const name = join(resultPath, 'log-' + new Date().toISOString().replaceAll(':', '') + '.json'); await writeFile(name, `[${this.#entries .map(e => JSON.stringify({ timeOffset: this.#msSinceStart(e), ...JSON.parse(e.json), }, undefined, ' ')) .join(',' + EOL)}]`); return name; } catch (e) { console.error(`Error saving log:`); console.error(e); console.log('Full log:'); this.#entries.forEach(entry => { console.log(`@${this.#msSinceStart(entry)}ms ${levelString(entry.level)} ${entry.message}`); if (entry.error) { console.log(entry.error); } }); return undefined; } } } function levelString(level) { switch (level) { case 'trace': return '[TRACE] '; case 'debug': return '[DEBUG] '; case 'info': return '[INFO] '; case 'warning': return '[WARNING]'; case 'error': return '[ERROR] '; case 'fatal': return '[FATAL] '; default: return ' '; } } class TestContext { log; get env() { return this.environment; } environment; emitted = []; timeShift = 0; constructor(env) { this.environment = { BEARER_PUBLIC_KEY: 'MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAESKk7sgjLJNz4erSkGiuFRQCUZiVELR4VjqrWS01kKxZSthAKuX5A4ib8ODd2le/4m99vBIKpDKWP6CT/LvhzcXstSxz4VaOkbczfo3VUvKREi0yUZLasKB5oQP2AGAyr', BEARER_PRIVATE_KEY: 'MIGkAgEBBDCuIjzsQ+q0iCuyEiLq9vFfZ6Lj6/vxlZDxLanGoO88yL9V0EsZbofwvpW4cb32++SgBwYFK4EEACKhZANiAARIqTuyCMsk3Ph6tKQaK4VFAJRmJUQtHhWOqtZLTWQrFlK2EAq5fkDiJvw4N3aV7/ib328EgqkMpY/oJP8u+HNxey1LHPhVo6RtzN+jdVS8pESLTJRktqwoHmhA/YAYDKs=', ...env, }; this.log = new MockLogger(); } now() { const d = new Date(); d.setUTCSeconds(d.getUTCSeconds() + this.timeShift); return d; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2V0dXAuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJzZXR1cC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQkFBK0I7QUFDL0IsT0FBTyxFQUdILGFBQWEsR0FJaEIsTUFBTSx3QkFBd0IsQ0FBQTtBQUMvQixPQUFPLEVBQStCLE9BQU8sRUFBRSxNQUFNLHlCQUF5QixDQUFBO0FBQzlFLE9BQU8sRUFBRSxLQUFLLEVBQUUsT0FBTyxFQUFFLFFBQVEsRUFBRSxTQUFTLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQTtBQUN0RSxPQUFPLEVBQUUsR0FBRyxFQUFFLE1BQU0sU0FBUyxDQUFBO0FBQzdCLE9BQU8sRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxRQUFRLEVBQUUsR0FBRyxFQUFFLE1BQU0sV0FBVyxDQUFBO0FBQ2xFLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQTtBQUM3QyxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sVUFBVSxDQUFBO0FBR3hDLE1BQU0sVUFBVSxLQUFLO0lBQ2pCLGdCQUFnQixFQUFFLENBQUE7SUFDbEIsTUFBTSxDQUFDLEtBQUssSUFBSSxFQUFFO1FBQ2QsTUFBTSxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUUsR0FBRyxNQUFNLFVBQVUsRUFBRSxDQUFBO1FBQzNDLE1BQU0sR0FBRyxHQUFHLE9BQU8sQ0FBQyxHQUFHLEVBQUUsQ0FBQTtRQUN6QixNQUFNLEtBQUssR0FBRyxDQUFDLE1BQU0sT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUNyQyxJQUFJLENBQUMsRUFBRSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsS0FBSyxLQUFLLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxDQUM3RCxDQUFBO1FBQ0QsS0FBSyxNQUFNLElBQUksSUFBSSxLQUFLLEVBQUUsQ0FBQztZQUN2QixNQUFNLElBQUksR0FBRyxRQUFRLENBQUMsSUFB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QUFDLGFBQWEsRUFBRSxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQTtRQUNuRCxPQUFPLENBQUMsQ0FBQTtJQUNaLENBQUM7Q0FDSiIsInNvdXJjZXNDb250ZW50IjpbIi8qIGVzbGludC1kaXNhYmxlIG5vLWNvbnNvbGUgKi9cbmltcG9ydCB7XG4gICAgQnVmZmVyZWRFdmVudCxcbiAgICBDbGllbnRJbmZvLFxuICAgIGNyZWF0ZUNvbnRleHQsXG4gICAgTG9nRW50cnksXG4gICAgTG9nTGV2ZWwsXG4gICAgTG9nVHJhbnNwb3J0LFxufSBmcm9tICdAcmlkZGFuY2UvaG9zdC9jb250ZXh0J1xuaW1wb3J0IHsgRnVsbENvbmZpZ3VyYXRpb24sIE1ldGFkYXRhLCBzZXRNZXRhIH0gZnJvbSAnQHJpZGRhbmNlL2hvc3QvcmVnaXN0cnknXG5pbXBvcnQgeyBta2RpciwgcmVhZGRpciwgcmVhZEZpbGUsIHdyaXRlRmlsZSB9IGZyb20gJ25vZGU6ZnMvcHJvbWlzZXMnXG5pbXBvcnQgeyBFT0wgfSBmcm9tICdub2RlOm9zJ1xuaW1wb3J0IHsgYmFzZW5hbWUsIGV4dG5hbWUsIGpvaW4sIHJlbGF0aXZlLCBzZXAgfSBmcm9tICdub2RlOnBhdGgnXG5pbXBvcnQgeyBwZXJmb3JtYW5jZSB9IGZyb20gJ25vZGU6cGVyZl9ob29rcydcbmltcG9ydCB7IHBhdGhUb0ZpbGVVUkwgfSBmcm9tICdub2RlOnVybCdcbmltcG9ydCB7IEVudmlyb25tZW50LCBKc29uIH0gZnJvbSAnLi4vY29udGV4dC5qcydcblxuZXhwb3J0IGZ1bmN0aW9uIHNldHVwKCkge1xuICAgIHNldHVwVGVzdENvbnRleHQoKVxuICAgIGJlZm9yZShhc3luYyAoKSA9PiB7XG4gICAgICAgIGNvbnN0IHsgbmFtZSwgY29uZmlnIH0gPSBhd2FpdCByZWFkQ29uZmlnKClcbiAgICAgICAgY29uc3QgZGlyID0gcHJvY2Vzcy5jd2QoKVxuICAgICAgICBjb25zdCBmaWxlcyA9IChhd2FpdCByZWFkZGlyKCcuJykpLmZpbHRlcihcbiAgICAgICAgICAgIGZpbGUgPT4gZXh0bmFtZShmaWxlKSA9PT0gJy50cycgJiYgIWZpbGUuZW5kc1dpdGgoJy5kLnRzJyksXG4gICAgICAgIClcbiAgICAgICAgZm9yIChjb25zdCBmaWxlIG9mIGZpbGVzKSB7XG4gICAgICAgICAgICBjb25zdCBiYXNlID0gYmFzZW5hbWUoZmlsZSwgJy50cycpXG4gICAgICAgICAgICBzZXRNZXRhKG5hbWUsIGJhc2UsIHVuZGVmaW5lZCwgY29uZmlnKVxuICAgICAgICAgICAgYXdhaXQgaW1wb3J0KHBhdGhUb0ZpbGVVUkwoam9pbihkaXIsIGJhc2UgKyAnLmpzJykpLnRvU3RyaW5nKCkpXG4gICAgICAgIH1cbiAgICB9KVxufVxuXG5hc3luYyBmdW5jdGlvbiByZWFkRW52KCkge1xuICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IGVudlRleHQgPSBhd2FpdCByZWFkRmlsZSgndGVzdC9lbnYudHh0JywgJ3V0Zi04JylcbiAgICAgICAgcmV0dXJuIE9iamVjdC5mcm9tRW50cmllcyhcbiAgICAgICAgICAgIGVudlRleHRcbiAgICAgICAgICAgICAgICAuc3BsaXQoJ1xcbicpXG4gICAgICAgICAgICAgICAgLmZpbHRlcihsID0+IGwubGVuZ3RoICE9PSAwICYmICFsLnN0YXJ0c1dpdGgoJyMnKSlcbiAgICAgICAgICAgICAgICAubWFwKGxpbmUgPT4ge1xuICAgICAgICAgICAgICAgICAgICBjb25zdCBpeCA9IGxpbmUuaW5kZXhPZignPScpXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBbbGluZS5zbGljZSgwLCBpeCkudHJpbSgpLCBsaW5lLnNsaWNlKGl4ICsgMSkudHJpbSgpXVxuICAgICAgICAgICAgICAgIH0pLFxuICAgICAgICApXG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgICBpZiAoKGUgYXMgeyBjb2RlPzogc3RyaW5nIH0pLmNvZGUgPT09ICdFTk9FTlQnKSB7XG4gICAgICAgICAgICByZXR1cm4ge31cbiAgICAgICAgfVxuICAgICAgICB0aHJvdyBlXG4gICAgfVxufVxuXG5hc3luYyBmdW5jdGlvbiByZWFkQ29uZmlnKCkge1xuICAgIGNvbnN0IHBhY2thZ2VKc29uID0gSlNPTi5wYXJzZShhd2FpdCByZWFkRmlsZSgncGFja2FnZS5qc29uJywgJ3V0Zi04JykpIGFzIHtcbiAgICAgICAgbmFtZTogc3RyaW5nXG4gICAgICAgIGNvbmZpZz86IG9iamVjdFxuICAgIH1cbiAgICByZXR1cm4gcGFja2FnZUpzb25cbn1cblxubGV0IHRlc3RDb250ZXh0OiBUZXN0Q29udGV4dCB8IHVuZGVmaW5lZFxuXG5mdW5jdGlvbiBzZXR1cFRlc3RDb250ZXh0KCkge1xuICAgIGJlZm9yZUVhY2goJ0NsZWFyIGxvZ2dlZCBlbnRyaWVzJywgYXN5bmMgKCkgPT4ge1xuICAgICAgICBjb25zdCBlbnYgPSBhd2FpdCByZWFkRW52KClcbiAgICAgICAgaWYgKHRlc3RDb250ZXh0KSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0NvbnRleHQgZXhpc3RzLicpXG4gICAgICAgIH1cbiAgICAgICAgdGVzdENvbnRleHQgPSBuZXcgVGVzdENvbnRleHQoZW52KVxuICAgIH0pXG5cbiAgICBhZnRlckVhY2goJ0NoZWNrIGxvZycsIGFzeW5jIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgaWYgKCF0ZXN0Q29udGV4dCkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdUZXN0IGNvbnRleHQgbG9zdC4nKVxuICAgICAgICB9XG4gICAgICAgIGNvbnN0IHRlc3QgPSB0aGlzLmN1cnJlbnRUZXN0XG4gICAgICAgIGlmICh0ZXN0KSB7XG4gICAgICAgICAgICBjb25zdCB0aXRsZSA9IHRlc3QuZnVsbFRpdGxlKClcbiAgICAgICAgICAgIGlmICh0ZXN0LmlzRmFpbGVkKCkpIHtcbiAgICAgICAgICAgICAgICBhd2FpdCB0ZXN0Q29udGV4dC5sb2cuZHVtcExvZyh0aXRsZSlcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmICh0ZXN0Q29udGV4dC5sb2cuZmFpbGVkKSB7XG4gICAgICAgICAgICAgICAgaWYgKCF0ZXN0LmlzRmFpbGVkKCkpIHtcbiAgICAgICAgICAgICAgICAgICAgYXdhaXQgdGVzdENvbnRleHQubG9nLmR1bXBMb2codGl0bGUpXG4gICAgICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAgICAgICAgICAgICAgIGBcIiR7dGl0bGV9XCIgcGFzc2VkIGJ1dCBzdWJzZXF1ZW50bHkgZmFpbGVkIGJlY2F1c2UgZXJyb3JzIHdhcyBsb2dnZWQgZHVyaW5nIHRoZSB0ZXN0LiBXcmFwIHRoZSB0ZXN0IGNvZGUgaW4gYWxsb3dFcnJvckxvZ3MgaWYgdGhlIGVycm9yIGxvZyBlbnRyaWVzIGFyZSBleHBlY3RlZC5gLFxuICAgICAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHRlc3RDb250ZXh0ID0gdW5kZWZpbmVkXG4gICAgfSlcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZU1vY2tDb250ZXh0KGNsaWVudDogQ2xpZW50SW5mbywgY29uZmlnPzogRnVsbENvbmZpZ3VyYXRpb24sIG1ldGE/OiBNZXRhZGF0YSkge1xuICAgIGNvbnN0IGN0eCA9IGdldFRlc3RDb250ZXh0KClcbiAgICByZXR1cm4gY3JlYXRlQ29udGV4dChcbiAgICAgICAgY2xpZW50LFxuICAgICAgICBbY3R4LmxvZ10sXG4gICAgICAgIHtcbiAgICAgICAgICAgIHB1Ymxpc2hSYXRlOiAxMDAsXG4gICAgICAgICAgICBzZW5kRXZlbnRzOiAodG9waWMsIGV2ZW50czogQnVmZmVyZWRFdmVudFtdKSA9PiB7XG4gICAgICAgICAgICAgICAgY3R4LmVtaXR0ZWQucHVzaChcbiAgICAgICAgICAgICAgICAgICAgLi4uZXZlbnRzLm1hcChlID0+ICh7XG4gICAgICAgICAgICAgICAgICAgICAgICB0b3BpYyxcbiAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6IGUubWV0YS50eXBlLFxuICAgICAgICAgICAgICAgICAgICAgICAgc3ViamVjdDogZS5tZXRhLnN1YmplY3QsXG4gICAgICAgICAgICAgICAgICAgICAgICBkYXRhOiBlLmpzb24sXG4gICAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlSWQ6IGUubWV0YS5pZCxcbiAgICAgICAgICAgICAgICAgICAgfSkpLFxuICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKClcbiAgICAgICAgICAgIH0sXG4gICAgICAgIH0sXG4gICAgICAgIHsgZGVmYXVsdDogMTUgfSxcbiAgICAgICAgbmV3IEFib3J0Q29udHJvbGxlcigpLFxuICAgICAgICBjb25maWcsXG4gICAgICAgIG1ldGEsXG4gICAgICAgIGN0eC5lbnYsXG4gICAgICAgICgpID0+IGN0eC5ub3coKSxcbiAgICApXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRUZXN0Q29udGV4dCgpOiBUZXN0Q29udGV4dCB7XG4gICAgaWYgKCF0ZXN0Q29udGV4dCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ05vIHRlc3QgaXMgcnVubmluZy4nKVxuICAgIH1cbiAgICByZXR1cm4gdGVzdENvbnRleHRcbn1cblxuY2xhc3MgTW9ja0xvZ2dlciBpbXBsZW1lbnRzIExvZ1RyYW5zcG9ydCB7XG4gICAgI2VudHJpZXM6IExvZ0VudHJ5W10gPSBbXVxuICAgIHJlYWRvbmx5ICNzdGFydFRpbWUgPSBNYXRoLnJvdW5kKHBlcmZvcm1hbmNlLm5vdygpICogMTBfMDAwKVxuICAgIGZhaWxPbkVycm9yTG9ncyA9IHRydWVcbiAgICBmYWlsZWQgPSBmYWxzZVxuXG4gICAgZ2V0RW50cmllcygpIHtcbiAgICAgICAgcmV0dXJuIFsuLi50aGlzLiNlbnRyaWVzXVxuICAgIH1cblxuICAgIGNsZWFyKCkge1xuICAgICAgICB0aGlzLiNlbnRyaWVzID0gW11cbiAgICAgICAgdGhpcy5mYWlsT25FcnJvckxvZ3MgPSB0cnVlXG4gICAgICAgIHRoaXMuZmFpbGVkID0gZmFsc2VcbiAgICB9XG5cbiAgICBzZW5kRW50cmllcyhlbnRyaWVzOiBMb2dFbnRyeVtdKSB7XG4gICAgICAgIGlmICh0aGlzLmZhaWxPbkVycm9yTG9ncyAmJiBlbnRyaWVzLnNvbWUoZSA9PiBlLmxldmVsID09PSAnZXJyb3InIHx8IGUubGV2ZWwgPT09ICdmYXRhbCcpKSB7XG4gICAgICAgICAgICB0aGlzLmZhaWxlZCA9IHRydWVcbiAgICAgICAgfVxuICAgICAgICB0aGlzLiNlbnRyaWVzLnB1c2goLi4uZW50cmllcylcbiAgICAgICAgcmV0dXJuIHVuZGVmaW5lZFxuICAgIH1cblxuICAgICNtc1NpbmNlU3RhcnQoZW50cnk6IExvZ0VudHJ5KSB7XG4gICAgICAgIHJldHVybiAoTWF0aC5yb3VuZChlbnRyeS50aW1lc3RhbXAgKiAxMF8wMDApIC0gdGhpcy4jc3RhcnRUaW1lKSAvIDEwXzAwMFxuICAgIH1cblxuICAgIGFzeW5jIGR1bXBMb2codGVzdFRpdGxlOiBzdHJpbmcpIHtcbiAgICAgICAgaWYgKHRoaXMuI2VudHJpZXMubGVuZ3RoICE9PSAwKSB7XG4gICAgICAgICAgICBjb25zdCBwID0gdGhpcy53cml0ZUxvZygpXG4gICAgICAgICAgICBjb25zdCBlcnJvcnMgPSB0aGlzLiNlbnRyaWVzLmZpbHRlcihlID0+IGUubGV2ZWwgPT09ICdmYXRhbCcgfHwgZS5sZXZlbCA9PT0gJ2Vycm9yJylcbiAgICAgICAgICAgIGlmIChlcnJvcnMubGVuZ3RoICE9PSAwKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcih0ZXN0VGl0bGUgKyAnIGVycm9yIGxvZzonKVxuICAgICAgICAgICAgICAgIGVycm9ycy5mb3JFYWNoKGUgPT4ge1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKFxuICAgICAgICAgICAgICAgICAgICAgICAgYEAke3RoaXMuI21zU2luY2VTdGFydChlKX1tcyAke2xldmVsU3RyaW5nKGUubGV2ZWwpfSAke2UubWVzc2FnZX1gLFxuICAgICAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICAgICAgIGlmIChlLmVycm9yKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKGUuZXJyb3IpXG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgY29uc3QgbG9nRmlsZSA9IGF3YWl0IHBcbiAgICAgICAgICAgIGlmIChsb2dGaWxlKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5pbmZvKFxuICAgICAgICAgICAgICAgICAgICBgRnVsbCBsb2cgb2YgXCIke3Rlc3RUaXRsZX1cIiBzYXZlZCB0byAuJHtzZXB9JHtyZWxhdGl2ZShwcm9jZXNzLmVudi5QUk9KRUNUX0RJUkVDVE9SWSA/PyBwcm9jZXNzLmN3ZCgpLCBsb2dGaWxlKX1gLFxuICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIGFzeW5jIHdyaXRlTG9nKCkge1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgY29uc3QgcmVzdWx0UGF0aCA9IGpvaW4oJ3Rlc3QnLCAncmVzdWx0cycpXG4gICAgICAgICAgICBhd2FpdCBta2RpcihyZXN1bHRQYXRoLCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KVxuICAgICAgICAgICAgY29uc3QgbmFtZSA9IGpvaW4oXG4gICAgICAgICAgICAgICAgcmVzdWx0UGF0aCxcbiAgICAgICAgICAgICAgICAnbG9nLScgKyBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCkucmVwbGFjZUFsbCgnOicsICcnKSArICcuanNvbicsXG4gICAgICAgICAgICApXG4gICAgICAgICAgICBhd2FpdCB3cml0ZUZpbGUoXG4gICAgICAgICAgICAgICAgbmFtZSxcbiAgICAgICAgICAgICAgICBgWyR7dGhpcy4jZW50cmllc1xuICAgICAgICAgICAgICAgICAgICAubWFwKGUgPT5cbiAgICAgICAgICAgICAgICAgICAgICAgIEpTT04uc3RyaW5naWZ5KFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGltZU9mZnNldDogdGhpcy4jbXNTaW5jZVN0YXJ0KGUpLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAuLi5KU09OLnBhcnNlKGUuanNvbiksXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB1bmRlZmluZWQsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgJyAgJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICksXG4gICAgICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgICAgICAgLmpvaW4oJywnICsgRU9MKX1dYCxcbiAgICAgICAgICAgIClcbiAgICAgICAgICAgIHJldHVybiBuYW1lXG4gICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoYEVycm9yIHNhdmluZyBsb2c6YClcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoZSlcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKCdGdWxsIGxvZzonKVxuICAgICAgICAgICAgdGhpcy4jZW50cmllcy5mb3JFYWNoKGVudHJ5ID0+IHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhcbiAgICAgICAgICAgICAgICAgICAgYEAke3RoaXMuI21zU2luY2VTdGFydChlbnRyeSl9bXMgJHtsZXZlbFN0cmluZyhlbnRyeS5sZXZlbCl9ICR7ZW50cnkubWVzc2FnZX1gLFxuICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgICBpZiAoZW50cnkuZXJyb3IpIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coZW50cnkuZXJyb3IpXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIHJldHVybiB1bmRlZmluZWRcbiAgICAgICAgfVxuICAgIH1cbn1cblxuZnVuY3Rpb24gbGV2ZWxTdHJpbmcobGV2ZWw6IExvZ0xldmVsKSB7XG4gICAgc3dpdGNoIChsZXZlbCkge1xuICAgICAgICBjYXNlICd0cmFjZSc6XG4gICAgICAgICAgICByZXR1cm4gJ1tUUkFDRV0gICdcbiAgICAgICAgY2FzZSAnZGVidWcnOlxuICAgICAgICAgICAgcmV0dXJuICdbREVCVUddICAnXG4gICAgICAgIGNhc2UgJ2luZm8nOlxuICAgICAgICAgICAgcmV0dXJuICdbSU5GT10gICAnXG4gICAgICAgIGNhc2UgJ3dhcm5pbmcnOlxuICAgICAgICAgICAgcmV0dXJuICdbV0FSTklOR10nXG4gICAgICAgIGNhc2UgJ2Vycm9yJzpcbiAgICAgICAgICAgIHJldHVybiAnW0VSUk9SXSAgJ1xuICAgICAgICBjYXNlICdmYXRhbCc6XG4gICAgICAgICAgICByZXR1cm4gJ1tGQVRBTF0gICdcbiAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICAgIHJldHVybiAnICAgICAgICAgJ1xuICAgIH1cbn1cblxudHlwZSBFdmVudCA9IHtcbiAgICB0b3BpYzogc3RyaW5nXG4gICAgdHlwZTogc3RyaW5nXG4gICAgc3ViamVjdDogc3RyaW5nXG4gICAgZGF0YT86IEpzb25cbiAgICBtZXNzYWdlSWQ6IHN0cmluZyB8IHVuZGVmaW5lZFxufVxuXG5jbGFzcyBUZXN0Q29udGV4dCB7XG4gICAgcmVhZG9ubHkgbG9nOiBNb2NrTG9nZ2VyXG5cbiAgICBnZXQgZW52KCkge1xuICAgICAgICByZXR1cm4gdGhpcy5lbnZpcm9ubWVudFxuICAgIH1cblxuICAgIGVudmlyb25tZW50OiB7IFtrZXk6IHN0cmluZ106IHN0cmluZyB9XG4gICAgZW1pdHRlZDogRXZlbnRbXSA9IFtdXG5cbiAgICB0aW1lU2hpZnQgPSAwXG5cbiAgICBjb25zdHJ1Y3RvcihlbnY6IEVudmlyb25tZW50KSB7XG4gICAgICAgIHRoaXMuZW52aXJvbm1lbnQgPSB7XG4gICAgICAgICAgICBCRUFSRVJfUFVCTElDX0tFWTpcbiAgICAgICAgICAgICAgICAnTUhZd0VBWUhLb1pJemowQ0FRWUZLNEVFQUNJRFlnQUVTS2s3c2dqTEpOejRlclNrR2l1RlJRQ1VaaVZFTFI0VmpxcldTMDFrS3haU3RoQUt1WDVBNGliOE9EZDJsZS80bTk5dkJJS3BES1dQNkNUL0x2aHpjWHN0U3h6NFZhT2tiY3pmbzNWVXZLUkVpMHlVWkxhc0tCNW9RUDJBR0F5cicsXG4gICAgICAgICAgICBCRUFSRVJfUFJJVkFURV9LRVk6XG4gICAgICAgICAgICAgICAgJ01JR2tBZ0VCQkRDdUlqenNRK3EwaUN1eUVpTHE5dkZmWjZMajYvdnhsWkR4TGFuR29PODh5TDlWMEVzWmJvZnd2cFc0Y2IzMisrU2dCd1lGSzRFRUFDS2haQU5pQUFSSXFUdXlDTXNrM1BoNnRLUWFLNFZGQUpSbUpVUXRIaFdPcXRaTFRXUXJGbEsyRUFxNWZrRGlKdnc0TjNhVjcvaWIzMjhFZ3FrTXBZL29KUDh1K0hOeGV5MUxIUGhWbzZSdHpOK2pkVlM4cEVTTFRKUmt0cXdvSG1oQS9ZQVlES3M9JyxcbiAgICAgICAgICAgIC4uLmVudixcbiAgICAgICAgfVxuICAgICAgICB0aGlzLmxvZyA9IG5ldyBNb2NrTG9nZ2VyKClcbiAgICB9XG5cbiAgICBub3coKTogRGF0ZSB7XG4gICAgICAgIGNvbnN0IGQgPSBuZXcgRGF0ZSgpXG4gICAgICAgIGQuc2V0VVRDU2Vjb25kcyhkLmdldFVUQ1NlY29uZHMoKSArIHRoaXMudGltZVNoaWZ0KVxuICAgICAgICByZXR1cm4gZFxuICAgIH1cbn1cbiJdfQ==