UNPKG

@riddance/service

Version:

Too much code slows you down, creates risks, increases maintainability burdens, confuses AI. So let's commit less of it.

212 lines 29.4 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'; 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, 'test-mock', config); await import(pathToFileURL(join(dir, base + '.js')).toString()); } }); } setup(); 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. Add using _ = allowErrorLogs() if the error log entries are expected.`); } } } testContext = undefined; }); } export function jsonRoundtrip(obj) { if (obj === undefined) { return undefined; } // eslint-disable-next-line unicorn/prefer-structured-clone return JSON.parse(JSON.stringify(obj)); } export function createMockContext(client, config, meta) { const ctx = getTestContext(); return createContext(client, [ctx.log], { sendEvent(topic, type, subject, data, messageId, signal) { signal.throwIfAborted(); ctx.emitted.push({ topic, type, subject, data: jsonRoundtrip(data), messageId, }); 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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2V0dXAuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJzZXR1cC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQkFBK0I7QUFDL0IsT0FBTyxFQUFjLGFBQWEsRUFBb0MsTUFBTSx3QkFBd0IsQ0FBQTtBQUNwRyxPQUFPLEVBQStCLE9BQU8sRUFBRSxNQUFNLHlCQUF5QixDQUFBO0FBQzlFLE9BQU8sRUFBRSxLQUFLLEVBQUUsT0FBTyxFQUFFLFFBQVEsRUFBRSxTQUFTLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQTtBQUN0RSxPQUFPLEVBQUUsR0FBRyxFQUFFLE1BQU0sU0FBUyxDQUFBO0FBQzdCLE9BQU8sRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxRQUFRLEVBQUUsR0FBRyxFQUFFLE1BQU0sV0FBVyxDQUFBO0FBQ2xFLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQTtBQUM3QyxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sVUFBVSxDQUFBO0FBU3hDLFNBQVMsS0FBSztJQUNWLGdCQUFnQixFQUFFLENBQUE7SUFDbEIsTUFBTSxDQUFDLEtBQUssSUFBSSxFQUFFO1FBQ2QsTUFBTSxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUUsR0FBRyxNQUFNLFVBQVUsRUFBRSxDQUFBO1FBQzNDLE1BQU0sR0FBRyxHQUFHLE9BQU8sQ0FBQyxHQUFHLEVBQUUsQ0FBQTtRQUN6QixNQUFNLEtBQUssR0FBRyxDQUFDLE1BQU0sT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUNyQyxJQUFJLENBQUMsRUFBRSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsS0FBSyxLQUFLLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxDQUM3RCxDQUFBO1FBQ0QsS0FBSyxNQUFNLElBQUksSUFBSSxLQUFLLEVBQUUsQ0FBQztZQUN2QixNQUFNLElBQUksR0FBRyxRQUFRLENBQUMsSUFBSSxFQUFF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RSxDQUFBO0lBQy9CLENBQUM7SUFFRCxHQUFHO1FBQ0MsTUFBTSxDQUFDLEdBQUcsSUFBSSxJQUFJLEVBQUUsQ0FBQTtRQUNwQixDQUFDLENBQUMsYUFBYSxDQUFDLENBQUMsQ0FBQyxhQUFhLEVBQUUsR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUE7UUFDbkQsT0FBTyxDQUFDLENBQUE7SUFDWixDQUFDO0NBQ0oiLCJzb3VyY2VzQ29udGVudCI6WyIvKiBlc2xpbnQtZGlzYWJsZSBuby1jb25zb2xlICovXG5pbXBvcnQgeyBDbGllbnRJbmZvLCBjcmVhdGVDb250ZXh0LCBMb2dFbnRyeSwgTG9nTGV2ZWwsIExvZ1RyYW5zcG9ydCB9IGZyb20gJ0ByaWRkYW5jZS9ob3N0L2NvbnRleHQnXG5pbXBvcnQgeyBGdWxsQ29uZmlndXJhdGlvbiwgTWV0YWRhdGEsIHNldE1ldGEgfSBmcm9tICdAcmlkZGFuY2UvaG9zdC9yZWdpc3RyeSdcbmltcG9ydCB7IG1rZGlyLCByZWFkZGlyLCByZWFkRmlsZSwgd3JpdGVGaWxlIH0gZnJvbSAnbm9kZTpmcy9wcm9taXNlcydcbmltcG9ydCB7IEVPTCB9IGZyb20gJ25vZGU6b3MnXG5pbXBvcnQgeyBiYXNlbmFtZSwgZXh0bmFtZSwgam9pbiwgcmVsYXRpdmUsIHNlcCB9IGZyb20gJ25vZGU6cGF0aCdcbmltcG9ydCB7IHBlcmZvcm1hbmNlIH0gZnJvbSAnbm9kZTpwZXJmX2hvb2tzJ1xuaW1wb3J0IHsgcGF0aFRvRmlsZVVSTCB9IGZyb20gJ25vZGU6dXJsJ1xuaW1wb3J0IHtcbiAgICBFbnZpcm9ubWVudCxcbiAgICBKc29uU2FmZU9iamVjdCxcbiAgICB0eXBlIEpzb25PYmplY3QsXG4gICAgdHlwZSBKc29uU2FmZSxcbiAgICB0eXBlIFN0cmluZ2lmaWVkLFxufSBmcm9tICcuLi9jb250ZXh0LmpzJ1xuXG5mdW5jdGlvbiBzZXR1cCgpIHtcbiAgICBzZXR1cFRlc3RDb250ZXh0KClcbiAgICBiZWZvcmUoYXN5bmMgKCkgPT4ge1xuICAgICAgICBjb25zdCB7IG5hbWUsIGNvbmZpZyB9ID0gYXdhaXQgcmVhZENvbmZpZygpXG4gICAgICAgIGNvbnN0IGRpciA9IHByb2Nlc3MuY3dkKClcbiAgICAgICAgY29uc3QgZmlsZXMgPSAoYXdhaXQgcmVhZGRpcignLicpKS5maWx0ZXIoXG4gICAgICAgICAgICBmaWxlID0+IGV4dG5hbWUoZmlsZSkgPT09ICcudHMnICYmICFmaWxlLmVuZHNXaXRoKCcuZC50cycpLFxuICAgICAgICApXG4gICAgICAgIGZvciAoY29uc3QgZmlsZSBvZiBmaWxlcykge1xuICAgICAgICAgICAgY29uc3QgYmFzZSA9IGJhc2VuYW1lKGZpbGUsICcudHMnKVxuICAgICAgICAgICAgc2V0TWV0YShuYW1lLCBiYXNlLCAndGVzdC1tb2NrJywgY29uZmlnKVxuICAgICAgICAgICAgYXdhaXQgaW1wb3J0KHBhdGhUb0ZpbGVVUkwoam9pbihkaXIsIGJhc2UgKyAnLmpzJykpLnRvU3RyaW5nKCkpXG4gICAgICAgIH1cbiAgICB9KVxufVxuXG5zZXR1cCgpXG5cbmFzeW5jIGZ1bmN0aW9uIHJlYWRFbnYoKSB7XG4gICAgdHJ5IHtcbiAgICAgICAgY29uc3QgZW52VGV4dCA9IGF3YWl0IHJlYWRGaWxlKCd0ZXN0L2Vudi50eHQnLCAndXRmLTgnKVxuICAgICAgICByZXR1cm4gT2JqZWN0LmZyb21FbnRyaWVzKFxuICAgICAgICAgICAgZW52VGV4dFxuICAgICAgICAgICAgICAgIC5zcGxpdCgnXFxuJylcbiAgICAgICAgICAgICAgICAuZmlsdGVyKGwgPT4gbC5sZW5ndGggIT09IDAgJiYgIWwuc3RhcnRzV2l0aCgnIycpKVxuICAgICAgICAgICAgICAgIC5tYXAobGluZSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IGl4ID0gbGluZS5pbmRleE9mKCc9JylcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIFtsaW5lLnNsaWNlKDAsIGl4KS50cmltKCksIGxpbmUuc2xpY2UoaXggKyAxKS50cmltKCldXG4gICAgICAgICAgICAgICAgfSksXG4gICAgICAgIClcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIGlmICgoZSBhcyB7IGNvZGU/OiBzdHJpbmcgfSkuY29kZSA9PT0gJ0VOT0VOVCcpIHtcbiAgICAgICAgICAgIHJldHVybiB7fVxuICAgICAgICB9XG4gICAgICAgIHRocm93IGVcbiAgICB9XG59XG5cbmFzeW5jIGZ1bmN0aW9uIHJlYWRDb25maWcoKSB7XG4gICAgY29uc3QgcGFja2FnZUpzb24gPSBKU09OLnBhcnNlKGF3YWl0IHJlYWRGaWxlKCdwYWNrYWdlLmpzb24nLCAndXRmLTgnKSkgYXMge1xuICAgICAgICBuYW1lOiBzdHJpbmdcbiAgICAgICAgY29uZmlnPzogb2JqZWN0XG4gICAgfVxuICAgIHJldHVybiBwYWNrYWdlSnNvblxufVxuXG5sZXQgdGVzdENvbnRleHQ6IFRlc3RDb250ZXh0IHwgdW5kZWZpbmVkXG5cbmZ1bmN0aW9uIHNldHVwVGVzdENvbnRleHQoKSB7XG4gICAgYmVmb3JlRWFjaCgnQ2xlYXIgbG9nZ2VkIGVudHJpZXMnLCBhc3luYyAoKSA9PiB7XG4gICAgICAgIGNvbnN0IGVudiA9IGF3YWl0IHJlYWRFbnYoKVxuICAgICAgICBpZiAodGVzdENvbnRleHQpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignQ29udGV4dCBleGlzdHMuJylcbiAgICAgICAgfVxuICAgICAgICB0ZXN0Q29udGV4dCA9IG5ldyBUZXN0Q29udGV4dChlbnYpXG4gICAgfSlcblxuICAgIGFmdGVyRWFjaCgnQ2hlY2sgbG9nJywgYXN5bmMgZnVuY3Rpb24gKCkge1xuICAgICAgICBpZiAoIXRlc3RDb250ZXh0KSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1Rlc3QgY29udGV4dCBsb3N0LicpXG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgdGVzdCA9IHRoaXMuY3VycmVudFRlc3RcbiAgICAgICAgaWYgKHRlc3QpIHtcbiAgICAgICAgICAgIGNvbnN0IHRpdGxlID0gdGVzdC5mdWxsVGl0bGUoKVxuICAgICAgICAgICAgaWYgKHRlc3QuaXNGYWlsZWQoKSkge1xuICAgICAgICAgICAgICAgIGF3YWl0IHRlc3RDb250ZXh0LmxvZy5kdW1wTG9nKHRpdGxlKVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKHRlc3RDb250ZXh0LmxvZy5mYWlsZWQpIHtcbiAgICAgICAgICAgICAgICBpZiAoIXRlc3QuaXNGYWlsZWQoKSkge1xuICAgICAgICAgICAgICAgICAgICBhd2FpdCB0ZXN0Q29udGV4dC5sb2cuZHVtcExvZyh0aXRsZSlcbiAgICAgICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICAgICAgICAgICAgICAgYFwiJHt0aXRsZX1cIiBwYXNzZWQgYnV0IHN1YnNlcXVlbnRseSBmYWlsZWQgYmVjYXVzZSBlcnJvcnMgd2FzIGxvZ2dlZCBkdXJpbmcgdGhlIHRlc3QuIEFkZCB1c2luZyBfID0gYWxsb3dFcnJvckxvZ3MoKSBpZiB0aGUgZXJyb3IgbG9nIGVudHJpZXMgYXJlIGV4cGVjdGVkLmAsXG4gICAgICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgdGVzdENvbnRleHQgPSB1bmRlZmluZWRcbiAgICB9KVxufVxuXG5leHBvcnQgZnVuY3Rpb24ganNvblJvdW5kdHJpcDxUIGV4dGVuZHMgSnNvblNhZmU+KG9iajogVCB8IHVuZGVmaW5lZCk6IFN0cmluZ2lmaWVkPFQ+IHwgdW5kZWZpbmVkIHtcbiAgICBpZiAob2JqID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgcmV0dXJuIHVuZGVmaW5lZFxuICAgIH1cbiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgdW5pY29ybi9wcmVmZXItc3RydWN0dXJlZC1jbG9uZVxuICAgIHJldHVybiBKU09OLnBhcnNlKEpTT04uc3RyaW5naWZ5KG9iaikpIGFzIFN0cmluZ2lmaWVkPFQ+XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVNb2NrQ29udGV4dChjbGllbnQ6IENsaWVudEluZm8sIGNvbmZpZz86IEZ1bGxDb25maWd1cmF0aW9uLCBtZXRhPzogTWV0YWRhdGEpIHtcbiAgICBjb25zdCBjdHggPSBnZXRUZXN0Q29udGV4dCgpXG4gICAgcmV0dXJuIGNyZWF0ZUNvbnRleHQoXG4gICAgICAgIGNsaWVudCxcbiAgICAgICAgW2N0eC5sb2ddLFxuICAgICAgICB7XG4gICAgICAgICAgICBzZW5kRXZlbnQoXG4gICAgICAgICAgICAgICAgdG9waWM6IHN0cmluZyxcbiAgICAgICAgICAgICAgICB0eXBlOiBzdHJpbmcsXG4gICAgICAgICAgICAgICAgc3ViamVjdDogc3RyaW5nLFxuICAgICAgICAgICAgICAgIGRhdGE6IEpzb25TYWZlT2JqZWN0IHwgdW5kZWZpbmVkLFxuICAgICAgICAgICAgICAgIG1lc3NhZ2VJZDogc3RyaW5nIHwgdW5kZWZpbmVkLFxuICAgICAgICAgICAgICAgIHNpZ25hbDogQWJvcnRTaWduYWwsXG4gICAgICAgICAgICApIHtcbiAgICAgICAgICAgICAgICBzaWduYWwudGhyb3dJZkFib3J0ZWQoKVxuICAgICAgICAgICAgICAgIGN0eC5lbWl0dGVkLnB1c2goe1xuICAgICAgICAgICAgICAgICAgICB0b3BpYyxcbiAgICAgICAgICAgICAgICAgICAgdHlwZSxcbiAgICAgICAgICAgICAgICAgICAgc3ViamVjdCxcbiAgICAgICAgICAgICAgICAgICAgZGF0YToganNvblJvdW5kdHJpcChkYXRhKSxcbiAgICAgICAgICAgICAgICAgICAgbWVzc2FnZUlkLFxuICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZSgpXG4gICAgICAgICAgICB9LFxuICAgICAgICB9LFxuICAgICAgICB7IGRlZmF1bHQ6IDE1IH0sXG4gICAgICAgIG5ldyBBYm9ydENvbnRyb2xsZXIoKSxcbiAgICAgICAgY29uZmlnLFxuICAgICAgICBtZXRhLFxuICAgICAgICBjdHguZW52LFxuICAgICAgICAoKSA9PiBjdHgubm93KCksXG4gICAgKVxufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0VGVzdENvbnRleHQoKTogVGVzdENvbnRleHQge1xuICAgIGlmICghdGVzdENvbnRleHQpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdObyB0ZXN0IGlzIHJ1bm5pbmcuJylcbiAgICB9XG4gICAgcmV0dXJuIHRlc3RDb250ZXh0XG59XG5cbmNsYXNzIE1vY2tMb2dnZXIgaW1wbGVtZW50cyBMb2dUcmFuc3BvcnQge1xuICAgICNlbnRyaWVzOiBMb2dFbnRyeVtdID0gW11cbiAgICByZWFkb25seSAjc3RhcnRUaW1lID0gTWF0aC5yb3VuZChwZXJmb3JtYW5jZS5ub3coKSAqIDEwXzAwMClcbiAgICBmYWlsT25FcnJvckxvZ3MgPSB0cnVlXG4gICAgZmFpbGVkID0gZmFsc2VcblxuICAgIGdldEVudHJpZXMoKSB7XG4gICAgICAgIHJldHVybiBbLi4udGhpcy4jZW50cmllc11cbiAgICB9XG5cbiAgICBjbGVhcigpIHtcbiAgICAgICAgdGhpcy4jZW50cmllcyA9IFtdXG4gICAgICAgIHRoaXMuZmFpbE9uRXJyb3JMb2dzID0gdHJ1ZVxuICAgICAgICB0aGlzLmZhaWxlZCA9IGZhbHNlXG4gICAgfVxuXG4gICAgc2VuZEVudHJpZXMoZW50cmllczogTG9nRW50cnlbXSkge1xuICAgICAgICBpZiAodGhpcy5mYWlsT25FcnJvckxvZ3MgJiYgZW50cmllcy5zb21lKGUgPT4gZS5sZXZlbCA9PT0gJ2Vycm9yJyB8fCBlLmxldmVsID09PSAnZmF0YWwnKSkge1xuICAgICAgICAgICAgdGhpcy5mYWlsZWQgPSB0cnVlXG4gICAgICAgIH1cbiAgICAgICAgdGhpcy4jZW50cmllcy5wdXNoKC4uLmVudHJpZXMpXG4gICAgICAgIHJldHVybiB1bmRlZmluZWRcbiAgICB9XG5cbiAgICAjbXNTaW5jZVN0YXJ0KGVudHJ5OiBMb2dFbnRyeSkge1xuICAgICAgICByZXR1cm4gKE1hdGgucm91bmQoZW50cnkudGltZXN0YW1wICogMTBfMDAwKSAtIHRoaXMuI3N0YXJ0VGltZSkgLyAxMF8wMDBcbiAgICB9XG5cbiAgICBhc3luYyBkdW1wTG9nKHRlc3RUaXRsZTogc3RyaW5nKSB7XG4gICAgICAgIGlmICh0aGlzLiNlbnRyaWVzLmxlbmd0aCAhPT0gMCkge1xuICAgICAgICAgICAgY29uc3QgcCA9IHRoaXMud3JpdGVMb2coKVxuICAgICAgICAgICAgY29uc3QgZXJyb3JzID0gdGhpcy4jZW50cmllcy5maWx0ZXIoZSA9PiBlLmxldmVsID09PSAnZmF0YWwnIHx8IGUubGV2ZWwgPT09ICdlcnJvcicpXG4gICAgICAgICAgICBpZiAoZXJyb3JzLmxlbmd0aCAhPT0gMCkge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IodGVzdFRpdGxlICsgJyBlcnJvciBsb2c6JylcbiAgICAgICAgICAgICAgICBlcnJvcnMuZm9yRWFjaChlID0+IHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcihcbiAgICAgICAgICAgICAgICAgICAgICAgIGBAJHt0aGlzLiNtc1NpbmNlU3RhcnQoZSl9bXMgJHtsZXZlbFN0cmluZyhlLmxldmVsKX0gJHtlLm1lc3NhZ2V9YCxcbiAgICAgICAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgICAgICAgICBpZiAoZS5lcnJvcikge1xuICAgICAgICAgICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcihlLmVycm9yKVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNvbnN0IGxvZ0ZpbGUgPSBhd2FpdCBwXG4gICAgICAgICAgICBpZiAobG9nRmlsZSkge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUuaW5mbyhcbiAgICAgICAgICAgICAgICAgICAgYEZ1bGwgbG9nIG9mIFwiJHt0ZXN0VGl0bGV9XCIgc2F2ZWQgdG8gLiR7c2VwfSR7cmVsYXRpdmUocHJvY2Vzcy5lbnYuUFJPSkVDVF9ESVJFQ1RPUlkgPz8gcHJvY2Vzcy5jd2QoKSwgbG9nRmlsZSl9YCxcbiAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBhc3luYyB3cml0ZUxvZygpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGNvbnN0IHJlc3VsdFBhdGggPSBqb2luKCd0ZXN0JywgJ3Jlc3VsdHMnKVxuICAgICAgICAgICAgYXdhaXQgbWtkaXIocmVzdWx0UGF0aCwgeyByZWN1cnNpdmU6IHRydWUgfSlcbiAgICAgICAgICAgIGNvbnN0IG5hbWUgPSBqb2luKFxuICAgICAgICAgICAgICAgIHJlc3VsdFBhdGgsXG4gICAgICAgICAgICAgICAgJ2xvZy0nICsgbmV3IERhdGUoKS50b0lTT1N0cmluZygpLnJlcGxhY2VBbGwoJzonLCAnJykgKyAnLmpzb24nLFxuICAgICAgICAgICAgKVxuICAgICAgICAgICAgYXdhaXQgd3JpdGVGaWxlKFxuICAgICAgICAgICAgICAgIG5hbWUsXG4gICAgICAgICAgICAgICAgYFske3RoaXMuI2VudHJpZXNcbiAgICAgICAgICAgICAgICAgICAgLm1hcChlID0+XG4gICAgICAgICAgICAgICAgICAgICAgICBKU09OLnN0cmluZ2lmeShcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpbWVPZmZzZXQ6IHRoaXMuI21zU2luY2VTdGFydChlKSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLi4uSlNPTi5wYXJzZShlLmpzb24pLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdW5kZWZpbmVkLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICcgICcsXG4gICAgICAgICAgICAgICAgICAgICAgICApLFxuICAgICAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICAgICAgIC5qb2luKCcsJyArIEVPTCl9XWAsXG4gICAgICAgICAgICApXG4gICAgICAgICAgICByZXR1cm4gbmFtZVxuICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKGBFcnJvciBzYXZpbmcgbG9nOmApXG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKGUpXG4gICAgICAgICAgICBjb25zb2xlLmxvZygnRnVsbCBsb2c6JylcbiAgICAgICAgICAgIHRoaXMuI2VudHJpZXMuZm9yRWFjaChlbnRyeSA9PiB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgICAgICAgICAgIGBAJHt0aGlzLiNtc1NpbmNlU3RhcnQoZW50cnkpfW1zICR7bGV2ZWxTdHJpbmcoZW50cnkubGV2ZWwpfSAke2VudHJ5Lm1lc3NhZ2V9YCxcbiAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICAgaWYgKGVudHJ5LmVycm9yKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKGVudHJ5LmVycm9yKVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pXG4gICAgICAgICAgICByZXR1cm4gdW5kZWZpbmVkXG4gICAgICAgIH1cbiAgICB9XG59XG5cbmZ1bmN0aW9uIGxldmVsU3RyaW5nKGxldmVsOiBMb2dMZXZlbCkge1xuICAgIHN3aXRjaCAobGV2ZWwpIHtcbiAgICAgICAgY2FzZSAndHJhY2UnOlxuICAgICAgICAgICAgcmV0dXJuICdbVFJBQ0VdICAnXG4gICAgICAgIGNhc2UgJ2RlYnVnJzpcbiAgICAgICAgICAgIHJldHVybiAnW0RFQlVHXSAgJ1xuICAgICAgICBjYXNlICdpbmZvJzpcbiAgICAgICAgICAgIHJldHVybiAnW0lORk9dICAgJ1xuICAgICAgICBjYXNlICd3YXJuaW5nJzpcbiAgICAgICAgICAgIHJldHVybiAnW1dBUk5JTkddJ1xuICAgICAgICBjYXNlICdlcnJvcic6XG4gICAgICAgICAgICByZXR1cm4gJ1tFUlJPUl0gICdcbiAgICAgICAgY2FzZSAnZmF0YWwnOlxuICAgICAgICAgICAgcmV0dXJuICdbRkFUQUxdICAnXG4gICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICByZXR1cm4gJyAgICAgICAgICdcbiAgICB9XG59XG5cbnR5cGUgRXZlbnQgPSB7XG4gICAgdG9waWM6IHN0cmluZ1xuICAgIHR5cGU6IHN0cmluZ1xuICAgIHN1YmplY3Q6IHN0cmluZ1xuICAgIGRhdGE/OiBKc29uT2JqZWN0XG4gICAgbWVzc2FnZUlkOiBzdHJpbmcgfCB1bmRlZmluZWRcbn1cblxuY2xhc3MgVGVzdENvbnRleHQge1xuICAgIHJlYWRvbmx5IGxvZzogTW9ja0xvZ2dlclxuXG4gICAgZ2V0IGVudigpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuZW52aXJvbm1lbnRcbiAgICB9XG5cbiAgICBlbnZpcm9ubWVudDogeyBba2V5OiBzdHJpbmddOiBzdHJpbmcgfVxuICAgIGVtaXR0ZWQ6IEV2ZW50W10gPSBbXVxuXG4gICAgdGltZVNoaWZ0ID0gMFxuXG4gICAgY29uc3RydWN0b3IoZW52OiBFbnZpcm9ubWVudCkge1xuICAgICAgICB0aGlzLmVudmlyb25tZW50ID0ge1xuICAgICAgICAgICAgQkVBUkVSX1BVQkxJQ19LRVk6XG4gICAgICAgICAgICAgICAgJ01IWXdFQVlIS29aSXpqMENBUVlGSzRFRUFDSURZZ0FFU0trN3NnakxKTno0ZXJTa0dpdUZSUUNVWmlWRUxSNFZqcXJXUzAxa0t4WlN0aEFLdVg1QTRpYjhPRGQybGUvNG05OXZCSUtwREtXUDZDVC9Mdmh6Y1hzdFN4ejRWYU9rYmN6Zm8zVlV2S1JFaTB5VVpMYXNLQjVvUVAyQUdBeXInLFxuICAgICAgICAgICAgQkVBUkVSX1BSSVZBVEVfS0VZOlxuICAgICAgICAgICAgICAgICdNSUdrQWdFQkJEQ3VJanpzUStxMGlDdXlFaUxxOXZGZlo2TGo2L3Z4bFpEeExhbkdvTzg4eUw5VjBFc1pib2Z3dnBXNGNiMzIrK1NnQndZRks0RUVBQ0toWkFOaUFBUklxVHV5Q01zazNQaDZ0S1FhSzRWRkFKUm1KVVF0SGhXT3F0WkxUV1FyRmxLMkVBcTVma0RpSnZ3NE4zYVY3L2liMzI4RWdxa01wWS9vSlA4dStITnhleTFMSFBoVm82UnR6TitqZFZTOHBFU0xUSlJrdHF3b0htaEEvWUFZREtzPScsXG4gICAgICAgICAgICAuLi5lbnYsXG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5sb2cgPSBuZXcgTW9ja0xvZ2dlcigpXG4gICAgfVxuXG4gICAgbm93KCk6IERhdGUge1xuICAgICAgICBjb25zdCBkID0gbmV3IERhdGUoKVxuICAgICAgICBkLnNldFVUQ1NlY29uZHMoZC5nZXRVVENTZWNvbmRzKCkgKyB0aGlzLnRpbWVTaGlmdClcbiAgICAgICAgcmV0dXJuIGRcbiAgICB9XG59XG4iXX0=