UNPKG

@nlabs/lex

Version:
429 lines (420 loc) 43.5 kB
import { execa } from "execa"; import { existsSync, readFileSync } from "fs"; import { sync as globSync } from "glob"; import { resolve as pathResolve } from "path"; import { LexConfig, getTypeScriptConfigPath } from "../../LexConfig.js"; import { createSpinner } from "../../utils/app.js"; import { getDirName, resolveBinaryPath } from "../../utils/file.js"; import { log } from "../../utils/log.js"; import { aiFunction } from "../ai/ai.js"; const detectESM = (cwd) => { const packageJsonPath = pathResolve(cwd, "package.json"); if (existsSync(packageJsonPath)) { try { const packageJsonContent = readFileSync(packageJsonPath, "utf8"); const packageJson = JSON.parse(packageJsonContent); return packageJson.type === "module"; } catch (_error) { return false; } } return false; }; const getTestFilePatterns = (testPathPattern) => { const defaultPatterns = ["**/*.test.*", "**/*.spec.*", "**/*.integration.*"]; if (!testPathPattern) { return defaultPatterns; } return [testPathPattern]; }; const findUncoveredSourceFiles = () => { const sourceFiles = globSync("src/**/*.{ts,tsx,js,jsx}", { cwd: process.cwd(), ignore: ["**/node_modules/**", "**/dist/**", "**/lib/**", "**/*.test.*", "**/*.spec.*"] }); const testFiles = globSync("**/*.{test,spec}.{ts,tsx,js,jsx}", { cwd: process.cwd(), ignore: ["**/node_modules/**", "**/dist/**", "**/lib/**"] }); return sourceFiles.filter((sourceFile) => { const baseName = sourceFile.replace(/\.[^/.]+$/, ""); return !testFiles.some((testFile) => testFile.includes(baseName)); }); }; const processTestResults = (outputFile) => { if (!outputFile) { return null; } try { const content = readFileSync(outputFile, "utf-8"); return JSON.parse(content); } catch (_error) { return null; } }; const test = async (options, args, callback = process.exit) => { const { analyze = false, aiAnalyze = false, aiDebug = false, aiGenerate = false, bail, changedFilesWithAncestor, changedSince, ci, cliName = "Lex", collectCoverageFrom, colors, config, debug = false, debugTests = false, detectOpenHandles, env, errorOnDeprecated, expand, forceExit, generate = false, json, lastCommit, listTests, logHeapUsage, maxWorkers, noStackTrace, notify, onlyChanged, outputFile, passWithNoTests, quiet, removeCache, runInBand, setup, showConfig, silent, testLocationInResults, testNamePattern, testPathPattern, update, useStderr, verbose, watch, watchAll } = options; const useGenerate = generate || aiGenerate; const useAnalyze = analyze || aiAnalyze; const useDebug = debugTests || aiDebug; log(`${cliName} testing...`, "info", quiet); const spinner = createSpinner(quiet); await LexConfig.parseConfig(options); const { useTypescript } = LexConfig.config; if (useTypescript) { const testConfigPath = getTypeScriptConfigPath("tsconfig.test.json"); if (existsSync(testConfigPath)) { log("Using tsconfig.test.json for testing...", "info", quiet); } else { LexConfig.checkTestTypescriptConfig(); } } if (useGenerate) { spinner.start("AI is analyzing code to generate test cases..."); try { const uncoveredFiles = findUncoveredSourceFiles(); if (uncoveredFiles.length > 0) { const targetFile = uncoveredFiles[0]; await aiFunction({ context: true, file: targetFile, prompt: `Generate Jest unit tests for this file: ${targetFile} ${readFileSync(targetFile, "utf-8")} Please create comprehensive tests that cover the main functionality. Include test fixtures and mocks where necessary.`, quiet, task: "test" }); spinner.succeed(`AI test generation suggestions provided for ${targetFile}`); } else { spinner.succeed("All source files appear to have corresponding test files"); } } catch (aiError) { spinner.fail("Could not generate AI test suggestions"); if (!quiet) { console.error("AI test generation error:", aiError); } } } const dirName = getDirName(); const projectJestBin = pathResolve(process.cwd(), "node_modules/.bin/jest"); let jestPath; if (existsSync(projectJestBin)) { jestPath = projectJestBin; } else { jestPath = resolveBinaryPath("jest"); } if (!jestPath) { log(` ${cliName} Error: Jest binary not found in Lex's node_modules or monorepo root`, "error", quiet); log("Please reinstall Lex or check your installation.", "info", quiet); return 1; } let jestConfigFile; let projectJestConfig = null; if (config) { jestConfigFile = config; } else { const projectJestConfigPath = pathResolve(process.cwd(), "jest.config.js"); const projectJestConfigCjsPath = pathResolve(process.cwd(), "jest.config.cjs"); const projectJestConfigMjsPath = pathResolve(process.cwd(), "jest.config.mjs"); const projectJestConfigJsonPath = pathResolve(process.cwd(), "jest.config.json"); if (existsSync(projectJestConfigPath)) { jestConfigFile = projectJestConfigPath; if (debug) { log(`Using project Jest config file: ${jestConfigFile}`, "info", quiet); } } else if (existsSync(projectJestConfigCjsPath)) { jestConfigFile = projectJestConfigCjsPath; if (debug) { log(`Using project Jest config file (CJS): ${jestConfigFile}`, "info", quiet); } } else if (existsSync(projectJestConfigMjsPath)) { jestConfigFile = projectJestConfigMjsPath; if (debug) { log(`Using project Jest config file (MJS): ${jestConfigFile}`, "info", quiet); } } else if (existsSync(projectJestConfigJsonPath)) { jestConfigFile = projectJestConfigJsonPath; if (debug) { log(`Using project Jest config file (JSON): ${jestConfigFile}`, "info", quiet); } } else { projectJestConfig = LexConfig.config.jest; const lexDir = LexConfig.getLexDir(); const lexJestConfig = pathResolve(lexDir, "jest.config.mjs"); if (debug) { log(`Looking for Jest config at: ${lexJestConfig}`, "info", quiet); log(`File exists: ${existsSync(lexJestConfig)}`, "info", quiet); } if (existsSync(lexJestConfig)) { jestConfigFile = lexJestConfig; if (projectJestConfig && Object.keys(projectJestConfig).length > 0) { if (debug) { log(`Using Lex Jest config with project Jest config from lex.config.cjs: ${jestConfigFile}`, "info", quiet); } } else { if (debug) { log(`Using Lex Jest config (no project Jest config found): ${jestConfigFile}`, "info", quiet); } } } else { if (debug) { log("No Jest config found in project or Lex", "warn", quiet); } jestConfigFile = ""; } } } const jestSetupFile = setup || pathResolve(process.cwd(), "jest.setup.js"); const jestOptions = ["--no-cache"]; const isESM = detectESM(process.cwd()); let nodeOptions = process.env.NODE_OPTIONS || ""; if (isESM) { if (!nodeOptions.includes("--experimental-vm-modules")) { nodeOptions = `${nodeOptions} --experimental-vm-modules`.trim(); } log("ESM project detected, using --experimental-vm-modules in NODE_OPTIONS", "info", quiet); } if (jestConfigFile) { jestOptions.push("--config", jestConfigFile); } if (bail) { jestOptions.push("--bail"); } if (changedFilesWithAncestor) { jestOptions.push("--changedFilesWithAncestor"); } if (changedSince) { jestOptions.push("--changedSince"); } if (ci) { jestOptions.push("--ci"); } if (collectCoverageFrom) { jestOptions.push("--collectCoverageFrom", collectCoverageFrom); } if (colors) { jestOptions.push("--colors"); } if (debug) { jestOptions.push("--debug"); } if (detectOpenHandles) { jestOptions.push("--detectOpenHandles"); } if (env) { jestOptions.push("--env"); } if (errorOnDeprecated) { jestOptions.push("--errorOnDeprecated"); } if (expand) { jestOptions.push("--expand"); } if (forceExit) { jestOptions.push("--forceExit"); } if (json) { jestOptions.push("--json"); } if (lastCommit) { jestOptions.push("--lastCommit"); } if (listTests) { jestOptions.push("--listTests"); } if (logHeapUsage) { jestOptions.push("--logHeapUsage"); } if (maxWorkers) { jestOptions.push("--maxWorkers", maxWorkers); } if (noStackTrace) { jestOptions.push("--noStackTrace"); } if (notify) { jestOptions.push("--notify"); } if (onlyChanged) { jestOptions.push("--onlyChanged"); } let tempOutputFile = outputFile; if ((useAnalyze || useDebug) && !outputFile) { tempOutputFile = ".lex-test-results.json"; jestOptions.push("--json", "--outputFile", tempOutputFile); } else if (outputFile) { jestOptions.push("--outputFile", outputFile); } if (passWithNoTests) { jestOptions.push("--passWithNoTests"); } if (runInBand) { jestOptions.push("--runInBand"); } if (showConfig) { jestOptions.push("--showConfig"); } if (silent) { jestOptions.push("--silent"); } if (testLocationInResults) { jestOptions.push("--testLocationInResults"); } if (testNamePattern) { jestOptions.push("--testNamePattern", testNamePattern); } if (testPathPattern) { jestOptions.push("--testPathPattern", testPathPattern); } if (useStderr) { jestOptions.push("--useStderr"); } if (verbose) { jestOptions.push("--verbose"); } if (watchAll) { jestOptions.push("--watchAll"); } if (removeCache) { jestOptions.push("--no-cache"); } if (jestSetupFile && existsSync(jestSetupFile)) { jestOptions.push(`--setupFilesAfterEnv=${jestSetupFile}`); } if (update) { jestOptions.push("--updateSnapshot"); } if (watch) { jestOptions.push("--watch", watch); } if (args) { jestOptions.push(...args); } if (debug) { log(`Jest options: ${jestOptions.join(" ")}`, "info", quiet); log(`NODE_OPTIONS: ${nodeOptions}`, "info", quiet); } try { const env2 = { ...process.env, NODE_OPTIONS: nodeOptions }; await execa(jestPath, jestOptions, { encoding: "utf8", stdio: "inherit", env: env2 }); spinner.succeed("Testing completed!"); if (useAnalyze) { spinner.start("AI is analyzing test coverage and suggesting improvements..."); try { const testResults = processTestResults(tempOutputFile); const filePatterns = getTestFilePatterns(testPathPattern); await aiFunction({ prompt: `Analyze these Jest test results and suggest test coverage improvements: ${JSON.stringify(testResults, null, 2)} Test patterns: ${filePatterns.join(", ")} Please provide: 1. Analysis of current coverage gaps 2. Suggestions for improving test cases 3. Recommendations for additional integration test scenarios 4. Best practices for increasing test effectiveness`, task: "optimize", context: true, quiet }); spinner.succeed("AI test analysis complete"); } catch (aiError) { spinner.fail("Could not generate AI test analysis"); if (!quiet) { console.error("AI analysis error:", aiError); } } } callback(0); return 0; } catch (error) { log(` ${cliName} Error: Check for unit test errors and/or coverage.`, "error", quiet); spinner.fail("Testing failed!"); if (useDebug) { spinner.start("AI is analyzing test failures..."); try { const testResults = processTestResults(tempOutputFile); await aiFunction({ context: true, prompt: `Debug these failed Jest tests and suggest fixes: ${JSON.stringify(error.message, null, 2)} Test results: ${JSON.stringify(testResults, null, 2)} Please provide: 1. Analysis of why the tests are failing 2. Specific suggestions to fix each failing test 3. Any potential issues with test fixtures or mocks 4. Code examples for solutions`, quiet, task: "help" }); spinner.succeed("AI debugging assistance complete"); } catch (aiError) { spinner.fail("Could not generate AI debugging assistance"); if (!quiet) { console.error("AI debugging error:", aiError); } } } callback(1); return 1; } }; var test_default = test; export { test_default as default, getTestFilePatterns, test }; //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vLi4vc3JjL2NvbW1hbmRzL3Rlc3QvdGVzdC50cyJdLAogICJzb3VyY2VzQ29udGVudCI6IFsiLyoqXG4gKiBDb3B5cmlnaHQgKGMpIDIwMTgtUHJlc2VudCwgTml0cm9nZW4gTGFicywgSW5jLlxuICogQ29weXJpZ2h0cyBsaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSB0aGUgYWNjb21wYW55aW5nIExJQ0VOU0UgZmlsZSBmb3IgdGVybXMuXG4gKi9cbmltcG9ydCB7ZXhlY2F9IGZyb20gJ2V4ZWNhJztcbmltcG9ydCB7ZXhpc3RzU3luYywgcmVhZEZpbGVTeW5jfSBmcm9tICdmcyc7XG5pbXBvcnQge3N5bmMgYXMgZ2xvYlN5bmN9IGZyb20gJ2dsb2InO1xuaW1wb3J0IHtyZXNvbHZlIGFzIHBhdGhSZXNvbHZlfSBmcm9tICdwYXRoJztcblxuaW1wb3J0IHtMZXhDb25maWcsIGdldFR5cGVTY3JpcHRDb25maWdQYXRofSBmcm9tICcuLi8uLi9MZXhDb25maWcuanMnO1xuaW1wb3J0IHtjcmVhdGVTcGlubmVyfSBmcm9tICcuLi8uLi91dGlscy9hcHAuanMnO1xuaW1wb3J0IHtnZXREaXJOYW1lLCByZXNvbHZlQmluYXJ5UGF0aH0gZnJvbSAnLi4vLi4vdXRpbHMvZmlsZS5qcyc7XG5pbXBvcnQge2xvZ30gZnJvbSAnLi4vLi4vdXRpbHMvbG9nLmpzJztcbmltcG9ydCB7YWlGdW5jdGlvbn0gZnJvbSAnLi4vYWkvYWkuanMnO1xuXG5jb25zdCBkZXRlY3RFU00gPSAoY3dkOiBzdHJpbmcpOiBib29sZWFuID0+IHtcbiAgY29uc3QgcGFja2FnZUpzb25QYXRoID0gcGF0aFJlc29sdmUoY3dkLCAncGFja2FnZS5qc29uJyk7XG5cbiAgaWYoZXhpc3RzU3luYyhwYWNrYWdlSnNvblBhdGgpKSB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHBhY2thZ2VKc29uQ29udGVudCA9IHJlYWRGaWxlU3luYyhwYWNrYWdlSnNvblBhdGgsICd1dGY4Jyk7XG4gICAgICBjb25zdCBwYWNrYWdlSnNvbiA9IEpTT04ucGFyc2UocGFja2FnZUpzb25Db250ZW50KTtcbiAgICAgIHJldHVybiBwYWNrYWdlSnNvbi50eXBlID09PSAnbW9kdWxlJztcbiAgICB9IGNhdGNoIChfZXJyb3IpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gIH1cblxuICByZXR1cm4gZmFsc2U7XG59O1xuXG5leHBvcnQgaW50ZXJmYWNlIFRlc3RPcHRpb25zIHtcbiAgcmVhZG9ubHkgYW5hbHl6ZT86IGJvb2xlYW47XG4gIHJlYWRvbmx5IGFpRGVidWc/OiBib29sZWFuO1xuICByZWFkb25seSBhaUdlbmVyYXRlPzogYm9vbGVhbjtcbiAgcmVhZG9ubHkgYWlBbmFseXplPzogYm9vbGVhbjtcbiAgcmVhZG9ubHkgYmFpbD86IGJvb2xlYW47XG4gIHJlYWRvbmx5IGNoYW5nZWRGaWxlc1dpdGhBbmNlc3Rvcj86IGJvb2xlYW47XG4gIHJlYWRvbmx5IGNoYW5nZWRTaW5jZT86IHN0cmluZztcbiAgcmVhZG9ubHkgY2k/OiBib29sZWFuO1xuICByZWFkb25seSBjbGlOYW1lPzogc3RyaW5nO1xuICByZWFkb25seSBjb2xsZWN0Q292ZXJhZ2VGcm9tPzogc3RyaW5nO1xuICByZWFkb25seSBjb2xvcnM/OiBib29sZWFuO1xuICByZWFkb25seSBjb25maWc/OiBzdHJpbmc7XG4gIHJlYWRvbmx5IGRlYnVnPzogYm9vbGVhbjtcbiAgcmVhZG9ubHkgZGVidWdUZXN0cz86IGJvb2xlYW47XG4gIHJlYWRvbmx5IGRldGVjdE9wZW5IYW5kbGVzPzogYm9vbGVhbjtcbiAgcmVhZG9ubHkgZW52Pzogc3RyaW5nO1xuICByZWFkb25seSBlcnJvck9uRGVwcmVjYXRlZD86IGJvb2xlYW47XG4gIHJlYWRvbmx5IGV4cGFuZD86IGJvb2xlYW47XG4gIHJlYWRvbmx5IGZvcmNlRXhpdD86IGJvb2xlYW47XG4gIHJlYWRvbmx5IGdlbmVyYXRlPzogYm9vbGVhbjtcbiAgcmVhZG9ubHkganNvbj86IGJvb2xlYW47XG4gIHJlYWRvbmx5IGxhc3RDb21taXQ/OiBib29sZWFuO1xuICByZWFkb25seSBsaXN0VGVzdHM/OiBib29sZWFuO1xuICByZWFkb25seSBsb2dIZWFwVXNhZ2U/OiBib29sZWFuO1xuICByZWFkb25seSBtYXhXb3JrZXJzPzogc3RyaW5nO1xuICByZWFkb25seSBub1N0YWNrVHJhY2U/OiBib29sZWFuO1xuICByZWFkb25seSBub3RpZnk/OiBib29sZWFuO1xuICByZWFkb25seSBvbmx5Q2hhbmdlZD86IGJvb2xlYW47XG4gIHJlYWRvbmx5IG91dHB1dEZpbGU/OiBzdHJpbmc7XG4gIHJlYWRvbmx5IHBhc3NXaXRoTm9UZXN0cz86IGJvb2xlYW47XG4gIHJlYWRvbmx5IHF1aWV0PzogYm9vbGVhbjtcbiAgcmVhZG9ubHkgcmVtb3ZlQ2FjaGU/OiBib29sZWFuO1xuICByZWFkb25seSBydW5JbkJhbmQ/OiBib29sZWFuO1xuICByZWFkb25seSBzZXR1cD86IHN0cmluZztcbiAgcmVhZG9ubHkgc2hvd0NvbmZpZz86IGJvb2xlYW47XG4gIHJlYWRvbmx5IHNpbGVudD86IGJvb2xlYW47XG4gIHJlYWRvbmx5IHRlc3RMb2NhdGlvbkluUmVzdWx0cz86IGJvb2xlYW47XG4gIHJlYWRvbmx5IHRlc3ROYW1lUGF0dGVybj86IHN0cmluZztcbiAgcmVhZG9ubHkgdGVzdFBhdGhQYXR0ZXJuPzogc3RyaW5nO1xuICByZWFkb25seSB1cGRhdGU/OiBib29sZWFuO1xuICByZWFkb25seSB1c2VTdGRlcnI/OiBib29sZWFuO1xuICByZWFkb25seSB2ZXJib3NlPzogYm9vbGVhbjtcbiAgcmVhZG9ubHkgd2F0Y2g/OiBzdHJpbmc7XG4gIHJlYWRvbmx5IHdhdGNoQWxsPzogYm9vbGVhbjtcbn1cblxuZXhwb3J0IHR5cGUgVGVzdENhbGxiYWNrID0gdHlwZW9mIHByb2Nlc3MuZXhpdDtcblxuZXhwb3J0IGNvbnN0IGdldFRlc3RGaWxlUGF0dGVybnMgPSAodGVzdFBhdGhQYXR0ZXJuPzogc3RyaW5nKTogc3RyaW5nW10gPT4ge1xuICBjb25zdCBkZWZhdWx0UGF0dGVybnMgPSBbJyoqLyoudGVzdC4qJywgJyoqLyouc3BlYy4qJywgJyoqLyouaW50ZWdyYXRpb24uKiddO1xuXG4gIGlmKCF0ZXN0UGF0aFBhdHRlcm4pIHtcbiAgICByZXR1cm4gZGVmYXVsdFBhdHRlcm5zO1xuICB9XG5cbiAgcmV0dXJuIFt0ZXN0UGF0aFBhdHRlcm5dO1xufTtcblxuY29uc3QgZmluZFVuY292ZXJlZFNvdXJjZUZpbGVzID0gKCk6IHN0cmluZ1tdID0+IHtcbiAgY29uc3Qgc291cmNlRmlsZXMgPSBnbG9iU3luYygnc3JjLyoqLyoue3RzLHRzeCxqcyxqc3h9Jywge1xuICAgIGN3ZDogcHJvY2Vzcy5jd2QoKSxcbiAgICBpZ25vcmU6IFsnKiovbm9kZV9tb2R1bGVzLyoqJywgJyoqL2Rpc3QvKionLCAnKiovbGliLyoqJywgJyoqLyoudGVzdC4qJywgJyoqLyouc3BlYy4qJ11cbiAgfSk7XG5cbiAgY29uc3QgdGVzdEZpbGVzID0gZ2xvYlN5bmMoJyoqLyoue3Rlc3Qsc3BlY30ue3RzLHRzeCxqcyxqc3h9Jywge1xuICAgIGN3ZDogcHJvY2Vzcy5jd2QoKSxcbiAgICBpZ25vcmU6IFsnKiovbm9kZV9tb2R1bGVzLyoqJywgJyoqL2Rpc3QvKionLCAnKiovbGliLyoqJ11cbiAgfSk7XG5cbiAgcmV0dXJuIHNvdXJjZUZpbGVzLmZpbHRlcigoc291cmNlRmlsZSkgPT4ge1xuICAgIGNvbnN0IGJhc2VOYW1lID0gc291cmNlRmlsZS5yZXBsYWNlKC9cXC5bXi8uXSskLywgJycpO1xuICAgIHJldHVybiAhdGVzdEZpbGVzLnNvbWUoKHRlc3RGaWxlKSA9PiB0ZXN0RmlsZS5pbmNsdWRlcyhiYXNlTmFtZSkpO1xuICB9KTtcbn07XG5cbmNvbnN0IHByb2Nlc3NUZXN0UmVzdWx0cyA9IChvdXRwdXRGaWxlPzogc3RyaW5nKTogYW55ID0+IHtcbiAgaWYoIW91dHB1dEZpbGUpIHtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuXG4gIHRyeSB7XG4gICAgY29uc3QgY29udGVudCA9IHJlYWRGaWxlU3luYyhvdXRwdXRGaWxlLCAndXRmLTgnKTtcbiAgICByZXR1cm4gSlNPTi5wYXJzZShjb250ZW50KTtcbiAgfSBjYXRjaCAoX2Vycm9yKSB7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cbn07XG5cbmV4cG9ydCBjb25zdCB0ZXN0ID0gYXN5bmMgKFxuICBvcHRpb25zOiBUZXN0T3B0aW9ucyxcbiAgYXJnczogc3RyaW5nW10sXG4gIGNhbGxiYWNrOiBUZXN0Q2FsbGJhY2sgPSBwcm9jZXNzLmV4aXRcbik6IFByb21pc2U8bnVtYmVyPiA9PiB7XG4gIGNvbnN0IHtcbiAgICBhbmFseXplID0gZmFsc2UsXG4gICAgYWlBbmFseXplID0gZmFsc2UsXG4gICAgYWlEZWJ1ZyA9IGZhbHNlLFxuICAgIGFpR2VuZXJhdGUgPSBmYWxzZSxcbiAgICBiYWlsLFxuICAgIGNoYW5nZWRGaWxlc1dpdGhBbmNlc3RvcixcbiAgICBjaGFuZ2VkU2luY2UsXG4gICAgY2ksXG4gICAgY2xpTmFtZSA9ICdMZXgnLFxuICAgIGNvbGxlY3RDb3ZlcmFnZUZyb20sXG4gICAgY29sb3JzLFxuICAgIGNvbmZpZyxcbiAgICBkZWJ1ZyA9IGZhbHNlLFxuICAgIGRlYnVnVGVzdHMgPSBmYWxzZSxcbiAgICBkZXRlY3RPcGVuSGFuZGxlcyxcbiAgICBlbnYsXG4gICAgZXJyb3JPbkRlcHJlY2F0ZWQsXG4gICAgZXhwYW5kLFxuICAgIGZvcmNlRXhpdCxcbiAgICBnZW5lcmF0ZSA9IGZhbHNlLFxuICAgIGpzb24sXG4gICAgbGFzdENvbW1pdCxcbiAgICBsaXN0VGVzdHMsXG4gICAgbG9nSGVhcFVzYWdlLFxuICAgIG1heFdvcmtlcnMsXG4gICAgbm9TdGFja1RyYWNlLFxuICAgIG5vdGlmeSxcbiAgICBvbmx5Q2hhbmdlZCxcbiAgICBvdXRwdXRGaWxlLFxuICAgIHBhc3NXaXRoTm9UZXN0cyxcbiAgICBxdWlldCxcbiAgICByZW1vdmVDYWNoZSxcbiAgICBydW5JbkJhbmQsXG4gICAgc2V0dXAsXG4gICAgc2hvd0NvbmZpZyxcbiAgICBzaWxlbnQsXG4gICAgdGVzdExvY2F0aW9uSW5SZXN1bHRzLFxuICAgIHRlc3ROYW1lUGF0dGVybixcbiAgICB0ZXN0UGF0aFBhdHRlcm4sXG4gICAgdXBkYXRlLFxuICAgIHVzZVN0ZGVycixcbiAgICB2ZXJib3NlLFxuICAgIHdhdGNoLFxuICAgIHdhdGNoQWxsXG4gIH0gPSBvcHRpb25zO1xuXG4gIGNvbnN0IHVzZUdlbmVyYXRlID0gZ2VuZXJhdGUgfHwgYWlHZW5lcmF0ZTtcbiAgY29uc3QgdXNlQW5hbHl6ZSA9IGFuYWx5emUgfHwgYWlBbmFseXplO1xuICBjb25zdCB1c2VEZWJ1ZyA9IGRlYnVnVGVzdHMgfHwgYWlEZWJ1ZztcblxuICBsb2coYCR7Y2xpTmFtZX0gdGVzdGluZy4uLmAsICdpbmZvJywgcXVpZXQpO1xuXG4gIGNvbnN0IHNwaW5uZXIgPSBjcmVhdGVTcGlubmVyKHF1aWV0KTtcblxuICBhd2FpdCBMZXhDb25maWcucGFyc2VDb25maWcob3B0aW9ucyk7XG5cbiAgY29uc3Qge3VzZVR5cGVzY3JpcHR9ID0gTGV4Q29uZmlnLmNvbmZpZztcblxuICBpZih1c2VUeXBlc2NyaXB0KSB7XG4gICAgY29uc3QgdGVzdENvbmZpZ1BhdGggPSBnZXRUeXBlU2NyaXB0Q29uZmlnUGF0aCgndHNjb25maWcudGVzdC5qc29uJyk7XG4gICAgaWYoZXhpc3RzU3luYyh0ZXN0Q29uZmlnUGF0aCkpIHtcbiAgICAgIGxvZygnVXNpbmcgdHNjb25maWcudGVzdC5qc29uIGZvciB0ZXN0aW5nLi4uJywgJ2luZm8nLCBxdWlldCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIExleENvbmZpZy5jaGVja1Rlc3RUeXBlc2NyaXB0Q29uZmlnKCk7XG4gICAgfVxuICB9XG5cbiAgaWYodXNlR2VuZXJhdGUpIHtcbiAgICBzcGlubmVyLnN0YXJ0KCdBSSBpcyBhbmFseXppbmcgY29kZSB0byBnZW5lcmF0ZSB0ZXN0IGNhc2VzLi4uJyk7XG5cbiAgICB0cnkge1xuICAgICAgY29uc3QgdW5jb3ZlcmVkRmlsZXMgPSBmaW5kVW5jb3ZlcmVkU291cmNlRmlsZXMoKTtcblxuICAgICAgaWYodW5jb3ZlcmVkRmlsZXMubGVuZ3RoID4gMCkge1xuICAgICAgICBjb25zdCB0YXJnZXRGaWxlID0gdW5jb3ZlcmVkRmlsZXNbMF07XG5cbiAgICAgICAgYXdhaXQgYWlGdW5jdGlvbih7XG4gICAgICAgICAgY29udGV4dDogdHJ1ZSxcbiAgICAgICAgICBmaWxlOiB0YXJnZXRGaWxlLFxuICAgICAgICAgIHByb21wdDogYEdlbmVyYXRlIEplc3QgdW5pdCB0ZXN0cyBmb3IgdGhpcyBmaWxlOiAke3RhcmdldEZpbGV9XFxuXFxuJHtyZWFkRmlsZVN5bmModGFyZ2V0RmlsZSwgJ3V0Zi04Jyl9XFxuXFxuUGxlYXNlIGNyZWF0ZSBjb21wcmVoZW5zaXZlIHRlc3RzIHRoYXQgY292ZXIgdGhlIG1haW4gZnVuY3Rpb25hbGl0eS4gSW5jbHVkZSB0ZXN0IGZpeHR1cmVzIGFuZCBtb2NrcyB3aGVyZSBuZWNlc3NhcnkuYCxcbiAgICAgICAgICBxdWlldCxcbiAgICAgICAgICB0YXNrOiAndGVzdCdcbiAgICAgICAgfSk7XG5cbiAgICAgICAgc3Bpbm5lci5zdWNjZWVkKGBBSSB0ZXN0IGdlbmVyYXRpb24gc3VnZ2VzdGlvbnMgcHJvdmlkZWQgZm9yICR7dGFyZ2V0RmlsZX1gKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHNwaW5uZXIuc3VjY2VlZCgnQWxsIHNvdXJjZSBmaWxlcyBhcHBlYXIgdG8gaGF2ZSBjb3JyZXNwb25kaW5nIHRlc3QgZmlsZXMnKTtcbiAgICAgIH1cbiAgICB9IGNhdGNoIChhaUVycm9yKSB7XG4gICAgICBzcGlubmVyLmZhaWwoJ0NvdWxkIG5vdCBnZW5lcmF0ZSBBSSB0ZXN0IHN1Z2dlc3Rpb25zJyk7XG4gICAgICBpZighcXVpZXQpIHtcbiAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWNvbnNvbGVcbiAgICAgICAgY29uc29sZS5lcnJvcignQUkgdGVzdCBnZW5lcmF0aW9uIGVycm9yOicsIGFpRXJyb3IpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIGNvbnN0IGRpck5hbWUgPSBnZXREaXJOYW1lKCk7XG5cbiAgY29uc3QgcHJvamVjdEplc3RCaW4gPSBwYXRoUmVzb2x2ZShwcm9jZXNzLmN3ZCgpLCAnbm9kZV9tb2R1bGVzLy5iaW4vamVzdCcpO1xuICBsZXQgamVzdFBhdGg6IHN0cmluZztcblxuICBpZihleGlzdHNTeW5jKHByb2plY3RKZXN0QmluKSkge1xuICAgIGplc3RQYXRoID0gcHJvamVjdEplc3RCaW47XG4gIH0gZWxzZSB7XG4gICAgamVzdFBhdGggPSByZXNvbHZlQmluYXJ5UGF0aCgnamVzdCcpO1xuICB9XG5cbiAgaWYoIWplc3RQYXRoKSB7XG4gICAgbG9nKGBcXG4ke2NsaU5hbWV9IEVycm9yOiBKZXN0IGJpbmFyeSBub3QgZm91bmQgaW4gTGV4J3Mgbm9kZV9tb2R1bGVzIG9yIG1vbm9yZXBvIHJvb3RgLCAnZXJyb3InLCBxdWlldCk7XG4gICAgbG9nKCdQbGVhc2UgcmVpbnN0YWxsIExleCBvciBjaGVjayB5b3VyIGluc3RhbGxhdGlvbi4nLCAnaW5mbycsIHF1aWV0KTtcbiAgICByZXR1cm4gMTtcbiAgfVxuXG4gIGxldCBqZXN0Q29uZmlnRmlsZTogc3RyaW5nO1xuICBsZXQgcHJvamVjdEplc3RDb25maWc6IGFueSA9IG51bGw7XG5cbiAgaWYoY29uZmlnKSB7XG4gICAgamVzdENvbmZpZ0ZpbGUgPSBjb25maWc7XG4gIH0gZWxzZSB7XG4gICAgY29uc3QgcHJvamVjdEplc3RDb25maWdQYXRoID0gcGF0aFJlc29sdmUocHJvY2Vzcy5jd2QoKSwgJ2plc3QuY29uZmlnLmpzJyk7XG4gICAgY29uc3QgcHJvamVjdEplc3RDb25maWdDanNQYXRoID0gcGF0aFJlc29sdmUocHJvY2Vzcy5jd2QoKSwgJ2plc3QuY29uZmlnLmNqcycpO1xuICAgIGNvbnN0IHByb2plY3RKZXN0Q29uZmlnTWpzUGF0aCA9IHBhdGhSZXNvbHZlKHByb2Nlc3MuY3dkKCksICdqZXN0LmNvbmZpZy5tanMnKTtcbiAgICBjb25zdCBwcm9qZWN0SmVzdENvbmZpZ0pzb25QYXRoID0gcGF0aFJlc29sdmUocHJvY2Vzcy5jd2QoKSwgJ2plc3QuY29uZmlnLmpzb24nKTtcblxuICAgIGlmKGV4aXN0c1N5bmMocHJvamVjdEplc3RDb25maWdQYXRoKSkge1xuICAgICAgamVzdENvbmZpZ0ZpbGUgPSBwcm9qZWN0SmVzdENvbmZpZ1BhdGg7XG4gICAgICBpZihkZWJ1Zykge1xuICAgICAgICBsb2coYFVzaW5nIHByb2plY3QgSmVzdCBjb25maWcgZmlsZTogJHtqZXN0Q29uZmlnRmlsZX1gLCAnaW5mbycsIHF1aWV0KTtcbiAgICAgIH1cbiAgICB9IGVsc2UgaWYoZXhpc3RzU3luYyhwcm9qZWN0SmVzdENvbmZpZ0Nqc1BhdGgpKSB7XG4gICAgICBqZXN0Q29uZmlnRmlsZSA9IHByb2plY3RKZXN0Q29uZmlnQ2pzUGF0aDtcbiAgICAgIGlmKGRlYnVnKSB7XG4gICAgICAgIGxvZyhgVXNpbmcgcHJvamVjdCBKZXN0IGNvbmZpZyBmaWxlIChDSlMpOiAke2plc3RDb25maWdGaWxlfWAsICdpbmZvJywgcXVpZXQpO1xuICAgICAgfVxuICAgIH0gZWxzZSBpZihleGlzdHNTeW5jKHByb2plY3RKZXN0Q29uZmlnTWpzUGF0aCkpIHtcbiAgICAgIGplc3RDb25maWdGaWxlID0gcHJvamVjdEplc3RDb25maWdNanNQYXRoO1xuICAgICAgaWYoZGVidWcpIHtcbiAgICAgICAgbG9nKGBVc2luZyBwcm9qZWN0IEplc3QgY29uZmlnIGZpbGUgKE1KUyk6ICR7amVzdENvbmZpZ0ZpbGV9YCwgJ2luZm8nLCBxdWlldCk7XG4gICAgICB9XG4gICAgfSBlbHNlIGlmKGV4aXN0c1N5bmMocHJvamVjdEplc3RDb25maWdKc29uUGF0aCkpIHtcbiAgICAgIGplc3RDb25maWdGaWxlID0gcHJvamVjdEplc3RDb25maWdKc29uUGF0aDtcbiAgICAgIGlmKGRlYnVnKSB7XG4gICAgICAgIGxvZyhgVXNpbmcgcHJvamVjdCBKZXN0IGNvbmZpZyBmaWxlIChKU09OKTogJHtqZXN0Q29uZmlnRmlsZX1gLCAnaW5mbycsIHF1aWV0KTtcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgLy8gTm8gSmVzdCBjb25maWcgZmlsZSBleGlzdHMgaW4gdGhlIHByb2plY3RcbiAgICAgIC8vIENoZWNrIGlmIHRoZXJlJ3MgYSBKZXN0IGNvbmZpZyBpbiBsZXguY29uZmlnLmNqc1xuICAgICAgcHJvamVjdEplc3RDb25maWcgPSBMZXhDb25maWcuY29uZmlnLmplc3Q7XG5cbiAgICAgIGNvbnN0IGxleERpciA9IExleENvbmZpZy5nZXRMZXhEaXIoKTtcbiAgICAgIGNvbnN0IGxleEplc3RDb25maWcgPSBwYXRoUmVzb2x2ZShsZXhEaXIsICdqZXN0LmNvbmZpZy5tanMnKTtcblxuICAgICAgaWYoZGVidWcpIHtcbiAgICAgICAgbG9nKGBMb29raW5nIGZvciBKZXN0IGNvbmZpZyBhdDogJHtsZXhKZXN0Q29uZmlnfWAsICdpbmZvJywgcXVpZXQpO1xuICAgICAgICBsb2coYEZpbGUgZXhpc3RzOiAke2V4aXN0c1N5bmMobGV4SmVzdENvbmZpZyl9YCwgJ2luZm8nLCBxdWlldCk7XG4gICAgICB9XG5cbiAgICAgIGlmKGV4aXN0c1N5bmMobGV4SmVzdENvbmZpZykpIHtcbiAgICAgICAgamVzdENvbmZpZ0ZpbGUgPSBsZXhKZXN0Q29uZmlnO1xuICAgICAgICBpZihwcm9qZWN0SmVzdENvbmZpZyAmJiBPYmplY3Qua2V5cyhwcm9qZWN0SmVzdENvbmZpZykubGVuZ3RoID4gMCkge1xuICAgICAgICAgIGlmKGRlYnVnKSB7XG4gICAgICAgICAgICBsb2coYFVzaW5nIExleCBKZXN0IGNvbmZpZyB3aXRoIHByb2plY3QgSmVzdCBjb25maWcgZnJvbSBsZXguY29uZmlnLmNqczogJHtqZXN0Q29uZmlnRmlsZX1gLCAnaW5mbycsIHF1aWV0KTtcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgaWYoZGVidWcpIHtcbiAgICAgICAgICAgIGxvZyhgVXNpbmcgTGV4IEplc3QgY29uZmlnIChubyBwcm9qZWN0IEplc3QgY29uZmlnIGZvdW5kKTogJHtqZXN0Q29uZmlnRmlsZX1gLCAnaW5mbycsIHF1aWV0KTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGlmKGRlYnVnKSB7XG4gICAgICAgICAgbG9nKCdObyBKZXN0IGNvbmZpZyBmb3VuZCBpbiBwcm9qZWN0IG9yIExleCcsICd3YXJuJywgcXVpZXQpO1xuICAgICAgICB9XG4gICAgICAgIGplc3RDb25maWdGaWxlID0gJyc7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgY29uc3QgamVzdFNldHVwRmlsZTogc3RyaW5nID0gc2V0dXAgfHwgcGF0aFJlc29sdmUocHJvY2Vzcy5jd2QoKSwgJ2plc3Quc2V0dXAuanMnKTtcbiAgY29uc3QgamVzdE9wdGlvbnM6IHN0cmluZ1tdID0gWyctLW5vLWNhY2hlJ107XG5cbiAgY29uc3QgaXNFU00gPSBkZXRlY3RFU00ocHJvY2Vzcy5jd2QoKSk7XG4gIGxldCBub2RlT3B0aW9ucyA9IHByb2Nlc3MuZW52Lk5PREVfT1BUSU9OUyB8fCAnJztcbiAgaWYoaXNFU00pIHtcbiAgICBpZighbm9kZU9wdGlvbnMuaW5jbHVkZXMoJy0tZXhwZXJpbWVudGFsLXZtLW1vZHVsZXMnKSkge1xuICAgICAgbm9kZU9wdGlvbnMgPSBgJHtub2RlT3B0aW9uc30gLS1leHBlcmltZW50YWwtdm0tbW9kdWxlc2AudHJpbSgpO1xuICAgIH1cbiAgICBsb2coJ0VTTSBwcm9qZWN0IGRldGVjdGVkLCB1c2luZyAtLWV4cGVyaW1lbnRhbC12bS1tb2R1bGVzIGluIE5PREVfT1BUSU9OUycsICdpbmZvJywgcXVpZXQpO1xuICB9XG5cbiAgaWYoamVzdENvbmZpZ0ZpbGUpIHtcbiAgICBqZXN0T3B0aW9ucy5wdXNoKCctLWNvbmZpZycsIGplc3RDb25maWdGaWxlKTtcbiAgfVxuXG4gIGlmKGJhaWwpIHtcbiAgICBqZXN0T3B0aW9ucy5wdXNoKCctLWJhaWwnKTtcbiAgfVxuXG4gIGlmKGNoYW5nZWRGaWxlc1dpdGhBbmNlc3Rvcikge1xuICAgIGplc3RPcHRpb25zLnB1c2goJy0tY2hhbmdlZEZpbGVzV2l0aEFuY2VzdG9yJyk7XG4gIH1cblxuICBpZihjaGFuZ2VkU2luY2UpIHtcbiAgICBqZXN0T3B0aW9ucy5wdXNoKCctLWNoYW5nZWRTaW5jZScpO1xuICB9XG5cbiAgaWYoY2kpIHtcbiAgICBqZXN0T3B0aW9ucy5wdXNoKCctLWNpJyk7XG4gIH1cblxuICBpZihjb2xsZWN0Q292ZXJhZ2VGcm9tKSB7XG4gICAgamVzdE9wdGlvbnMucHVzaCgnLS1jb2xsZWN0Q292ZXJhZ2VGcm9tJywgY29sbGVjdENvdmVyYWdlRnJvbSk7XG4gIH1cblxuICBpZihjb2xvcnMpIHtcbiAgICBqZXN0T3B0aW9ucy5wdXNoKCctLWNvbG9ycycpO1xuICB9XG5cbiAgaWYoZGVidWcpIHtcbiAgICBqZXN0T3B0aW9ucy5wdXNoKCctLWRlYnVnJyk7XG4gIH1cblxuICBpZihkZXRlY3RPcGVuSGFuZGxlcykge1xuICAgIGplc3RPcHRpb25zLnB1c2goJy0tZGV0ZWN0T3BlbkhhbmRsZXMnKTtcbiAgfVxuXG4gIGlmKGVudikge1xuICAgIGplc3RPcHRpb25zLnB1c2goJy0tZW52Jyk7XG4gIH1cblxuICBpZihlcnJvck9uRGVwcmVjYXRlZCkge1xuICAgIGplc3RPcHRpb25zLnB1c2goJy0tZXJyb3JPbkRlcHJlY2F0ZWQnKTtcbiAgfVxuXG4gIGlmKGV4cGFuZCkge1xuICAgIGplc3RPcHRpb25zLnB1c2goJy0tZXhwYW5kJyk7XG4gIH1cblxuICBpZihmb3JjZUV4aXQpIHtcbiAgICBqZXN0T3B0aW9ucy5wdXNoKCctLWZvcmNlRXhpdCcpO1xuICB9XG5cbiAgaWYoanNvbikge1xuICAgIGplc3RPcHRpb25zLnB1c2goJy0tanNvbicpO1xuICB9XG5cbiAgaWYobGFzdENvbW1pdCkge1xuICAgIGplc3RPcHRpb25zLnB1c2goJy0tbGFzdENvbW1pdCcpO1xuICB9XG5cbiAgaWYobGlzdFRlc3RzKSB7XG4gICAgamVzdE9wdGlvbnMucHVzaCgnLS1saXN0VGVzdHMnKTtcbiAgfVxuXG4gIGlmKGxvZ0hlYXBVc2FnZSkge1xuICAgIGplc3RPcHRpb25zLnB1c2goJy0tbG9nSGVhcFVzYWdlJyk7XG4gIH1cblxuICBpZihtYXhXb3JrZXJzKSB7XG4gICAgamVzdE9wdGlvbnMucHVzaCgnLS1tYXhXb3JrZXJzJywgbWF4V29ya2Vycyk7XG4gIH1cblxuICBpZihub1N0YWNrVHJhY2UpIHtcbiAgICBqZXN0T3B0aW9ucy5wdXNoKCctLW5vU3RhY2tUcmFjZScpO1xuICB9XG5cbiAgaWYobm90aWZ5KSB7XG4gICAgamVzdE9wdGlvbnMucHVzaCgnLS1ub3RpZnknKTtcbiAgfVxuXG4gIGlmKG9ubHlDaGFuZ2VkKSB7XG4gICAgamVzdE9wdGlvbnMucHVzaCgnLS1vbmx5Q2hhbmdlZCcpO1xuICB9XG5cbiAgbGV0IHRlbXBPdXRwdXRGaWxlID0gb3V0cHV0RmlsZTtcblxuICBpZigodXNlQW5hbHl6ZSB8fCB1c2VEZWJ1ZykgJiYgIW91dHB1dEZpbGUpIHtcbiAgICB0ZW1wT3V0cHV0RmlsZSA9ICcubGV4LXRlc3QtcmVzdWx0cy5qc29uJztcbiAgICBqZXN0T3B0aW9ucy5wdXNoKCctLWpzb24nLCAnLS1vdXRwdXRGaWxlJywgdGVtcE91dHB1dEZpbGUpO1xuICB9IGVsc2UgaWYob3V0cHV0RmlsZSkge1xuICAgIGplc3RPcHRpb25zLnB1c2goJy0tb3V0cHV0RmlsZScsIG91dHB1dEZpbGUpO1xuICB9XG5cbiAgaWYocGFzc1dpdGhOb1Rlc3RzKSB7XG4gICAgamVzdE9wdGlvbnMucHVzaCgnLS1wYXNzV2l0aE5vVGVzdHMnKTtcbiAgfVxuXG4gIGlmKHJ1bkluQmFuZCkge1xuICAgIGplc3RPcHRpb25zLnB1c2goJy0tcnVuSW5CYW5kJyk7XG4gIH1cblxuICBpZihzaG93Q29uZmlnKSB7XG4gICAgamVzdE9wdGlvbnMucHVzaCgnLS1zaG93Q29uZmlnJyk7XG4gIH1cblxuICBpZihzaWxlbnQpIHtcbiAgICBqZXN0T3B0aW9ucy5wdXNoKCctLXNpbGVudCcpO1xuICB9XG5cbiAgaWYodGVzdExvY2F0aW9uSW5SZXN1bHRzKSB7XG4gICAgamVzdE9wdGlvbnMucHVzaCgnLS10ZXN0TG9jYXRpb25JblJlc3VsdHMnKTtcbiAgfVxuXG4gIGlmKHRlc3ROYW1lUGF0dGVybikge1xuICAgIGplc3RPcHRpb25zLnB1c2goJy0tdGVzdE5hbWVQYXR0ZXJuJywgdGVzdE5hbWVQYXR0ZXJuKTtcbiAgfVxuXG4gIGlmKHRlc3RQYXRoUGF0dGVybikge1xuICAgIGplc3RPcHRpb25zLnB1c2goJy0tdGVzdFBhdGhQYXR0ZXJuJywgdGVzdFBhdGhQYXR0ZXJuKTtcbiAgfVxuXG4gIGlmKHVzZVN0ZGVycikge1xuICAgIGplc3RPcHRpb25zLnB1c2goJy0tdXNlU3RkZXJyJyk7XG4gIH1cblxuICBpZih2ZXJib3NlKSB7XG4gICAgamVzdE9wdGlvbnMucHVzaCgnLS12ZXJib3NlJyk7XG4gIH1cblxuICBpZih3YXRjaEFsbCkge1xuICAgIGplc3RPcHRpb25zLnB1c2goJy0td2F0Y2hBbGwnKTtcbiAgfVxuXG4gIGlmKHJlbW92ZUNhY2hlKSB7XG4gICAgamVzdE9wdGlvbnMucHVzaCgnLS1uby1jYWNoZScpO1xuICB9XG5cbiAgaWYoamVzdFNldHVwRmlsZSAmJiBleGlzdHNTeW5jKGplc3RTZXR1cEZpbGUpKSB7XG4gICAgamVzdE9wdGlvbnMucHVzaChgLS1zZXR1cEZpbGVzQWZ0ZXJFbnY9JHtqZXN0U2V0dXBGaWxlfWApO1xuICB9XG5cbiAgaWYodXBkYXRlKSB7XG4gICAgamVzdE9wdGlvbnMucHVzaCgnLS11cGRhdGVTbmFwc2hvdCcpO1xuICB9XG5cbiAgaWYod2F0Y2gpIHtcbiAgICBqZXN0T3B0aW9ucy5wdXNoKCctLXdhdGNoJywgd2F0Y2gpO1xuICB9XG5cbiAgaWYoYXJncykge1xuICAgIGplc3RPcHRpb25zLnB1c2goLi4uYXJncyk7XG4gIH1cblxuICBpZihkZWJ1Zykge1xuICAgIGxvZyhgSmVzdCBvcHRpb25zOiAke2plc3RPcHRpb25zLmpvaW4oJyAnKX1gLCAnaW5mbycsIHF1aWV0KTtcbiAgICBsb2coYE5PREVfT1BUSU9OUzogJHtub2RlT3B0aW9uc31gLCAnaW5mbycsIHF1aWV0KTtcbiAgfVxuXG4gIHRyeSB7XG4gICAgY29uc3QgZW52OiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0ge1xuICAgICAgLi4ucHJvY2Vzcy5lbnYsXG4gICAgICBOT0RFX09QVElPTlM6IG5vZGVPcHRpb25zXG4gICAgfTtcblxuICAgIGF3YWl0IGV4ZWNhKGplc3RQYXRoLCBqZXN0T3B0aW9ucywge1xuICAgICAgZW5jb2Rpbmc6ICd1dGY4JyxcbiAgICAgIHN0ZGlvOiAnaW5oZXJpdCcsXG4gICAgICBlbnZcbiAgICB9KTtcblxuICAgIHNwaW5uZXIuc3VjY2VlZCgnVGVzdGluZyBjb21wbGV0ZWQhJyk7XG5cbiAgICBpZih1c2VBbmFseXplKSB7XG4gICAgICBzcGlubmVyLnN0YXJ0KCdBSSBpcyBhbmFseXppbmcgdGVzdCBjb3ZlcmFnZSBhbmQgc3VnZ2VzdGluZyBpbXByb3ZlbWVudHMuLi4nKTtcblxuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgdGVzdFJlc3VsdHMgPSBwcm9jZXNzVGVzdFJlc3VsdHModGVtcE91dHB1dEZpbGUpO1xuICAgICAgICBjb25zdCBmaWxlUGF0dGVybnMgPSBnZXRUZXN0RmlsZVBhdHRlcm5zKHRlc3RQYXRoUGF0dGVybik7XG5cbiAgICAgICAgYXdhaXQgYWlGdW5jdGlvbih7XG4gICAgICAgICAgcHJvbXB0OiBgQW5hbHl6ZSB0aGVzZSBKZXN0IHRlc3QgcmVzdWx0cyBhbmQgc3VnZ2VzdCB0ZXN0IGNvdmVyYWdlIGltcHJvdmVtZW50czpcblxuJHtKU09OLnN0cmluZ2lmeSh0ZXN0UmVzdWx0cywgbnVsbCwgMil9XG5cblRlc3QgcGF0dGVybnM6ICR7ZmlsZVBhdHRlcm5zLmpvaW4oJywgJyl9XG5cblBsZWFzZSBwcm92aWRlOlxuMS4gQW5hbHlzaXMgb2YgY3VycmVudCBjb3ZlcmFnZSBnYXBzXG4yLiBTdWdnZXN0aW9ucyBmb3IgaW1wcm92aW5nIHRlc3QgY2FzZXNcbjMuIFJlY29tbWVuZGF0aW9ucyBmb3IgYWRkaXRpb25hbCBpbnRlZ3JhdGlvbiB0ZXN0IHNjZW5hcmlvc1xuNC4gQmVzdCBwcmFjdGljZXMgZm9yIGluY3JlYXNpbmcgdGVzdCBlZmZlY3RpdmVuZXNzYCxcbiAgICAgICAgICB0YXNrOiAnb3B0aW1pemUnLFxuICAgICAgICAgIGNvbnRleHQ6IHRydWUsXG4gICAgICAgICAgcXVpZXRcbiAgICAgICAgfSk7XG5cbiAgICAgICAgc3Bpbm5lci5zdWNjZWVkKCdBSSB0ZXN0IGFuYWx5c2lzIGNvbXBsZXRlJyk7XG4gICAgICB9IGNhdGNoIChhaUVycm9yKSB7XG4gICAgICAgIHNwaW5uZXIuZmFpbCgnQ291bGQgbm90IGdlbmVyYXRlIEFJIHRlc3QgYW5hbHlzaXMnKTtcbiAgICAgICAgaWYoIXF1aWV0KSB7XG4gICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWNvbnNvbGVcbiAgICAgICAgICBjb25zb2xlLmVycm9yKCdBSSBhbmFseXNpcyBlcnJvcjonLCBhaUVycm9yKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIGNhbGxiYWNrKDApO1xuICAgIHJldHVybiAwO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGxvZyhgXFxuJHtjbGlOYW1lfSBFcnJvcjogQ2hlY2sgZm9yIHVuaXQgdGVzdCBlcnJvcnMgYW5kL29yIGNvdmVyYWdlLmAsICdlcnJvcicsIHF1aWV0KTtcblxuICAgIHNwaW5uZXIuZmFpbCgnVGVzdGluZyBmYWlsZWQhJyk7XG5cbiAgICBpZih1c2VEZWJ1Zykge1xuICAgICAgc3Bpbm5lci5zdGFydCgnQUkgaXMgYW5hbHl6aW5nIHRlc3QgZmFpbHVyZXMuLi4nKTtcblxuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgdGVzdFJlc3VsdHMgPSBwcm9jZXNzVGVzdFJlc3VsdHModGVtcE91dHB1dEZpbGUpO1xuXG4gICAgICAgIGF3YWl0IGFpRnVuY3Rpb24oe1xuICAgICAgICAgIGNvbnRleHQ6IHRydWUsXG4gICAgICAgICAgcHJvbXB0OiBgRGVidWcgdGhlc2UgZmFpbGVkIEplc3QgdGVzdHMgYW5kIHN1Z2dlc3QgZml4ZXM6XG5cbiR7SlNPTi5zdHJpbmdpZnkoZXJyb3IubWVzc2FnZSwgbnVsbCwgMil9XG5cblRlc3QgcmVzdWx0czogJHtKU09OLnN0cmluZ2lmeSh0ZXN0UmVzdWx0cywgbnVsbCwgMil9XG5cblBsZWFzZSBwcm92aWRlOlxuMS4gQW5hbHlzaXMgb2Ygd2h5IHRoZSB0ZXN0cyBhcmUgZmFpbGluZ1xuMi4gU3BlY2lmaWMgc3VnZ2VzdGlvbnMgdG8gZml4IGVhY2ggZmFpbGluZyB0ZXN0XG4zLiBBbnkgcG90ZW50aWFsIGlzc3VlcyB3aXRoIHRlc3QgZml4dHVyZXMgb3IgbW9ja3NcbjQuIENvZGUgZXhhbXBsZXMgZm9yIHNvbHV0aW9uc2AsXG4gICAgICAgICAgcXVpZXQsXG4gICAgICAgICAgdGFzazogJ2hlbHAnXG4gICAgICAgIH0pO1xuXG4gICAgICAgIHNwaW5uZXIuc3VjY2VlZCgnQUkgZGVidWdnaW5nIGFzc2lzdGFuY2UgY29tcGxldGUnKTtcbiAgICAgIH0gY2F0Y2ggKGFpRXJyb3IpIHtcbiAgICAgICAgc3Bpbm5lci5mYWlsKCdDb3VsZCBub3QgZ2VuZXJhdGUgQUkgZGVidWdnaW5nIGFzc2lzdGFuY2UnKTtcbiAgICAgICAgaWYoIXF1aWV0KSB7XG4gICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWNvbnNvbGVcbiAgICAgICAgICBjb25zb2xlLmVycm9yKCdBSSBkZWJ1Z2dpbmcgZXJyb3I6JywgYWlFcnJvcik7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICBjYWxsYmFjaygxKTtcbiAgICByZXR1cm4gMTtcbiAgfVxufTtcblxuZXhwb3J0IGRlZmF1bHQgdGVzdDsiXSwKICAibWFwcGluZ3MiOiAiQUFJQSxTQUFRLGFBQVk7QUFDcEIsU0FBUSxZQUFZLG9CQUFtQjtBQUN2QyxTQUFRLFFBQVEsZ0JBQWU7QUFDL0IsU0FBUSxXQUFXLG1CQUFrQjtBQUVyQyxTQUFRLFdBQVcsK0JBQThCO0FBQ2pELFNBQVEscUJBQW9CO0FBQzVCLFNBQVEsWUFBWSx5QkFBd0I7QUFDNUMsU0FBUSxXQUFVO0FBQ2xCLFNBQVEsa0JBQWlCO0FBRXpCLE1BQU0sWUFBWSxDQUFDLFFBQXlCO0FBQzFDLFFBQU0sa0JBQWtCLFlBQVksS0FBSyxjQUFjO0FBRXZELE1BQUcsV0FBVyxlQUFlLEdBQUc7QUFDOUIsUUFBSTtBQUNGLFlBQU0scUJBQXFCLGFBQWEsaUJBQWlCLE1BQU07QUFDL0QsWUFBTSxjQUFjLEtBQUssTUFBTSxrQkFBa0I7QUFDakQsYUFBTyxZQUFZLFNBQVM7QUFBQSxJQUM5QixTQUFTLFFBQVE7QUFDZixhQUFPO0FBQUEsSUFDVDtBQUFBLEVBQ0Y7QUFFQSxTQUFPO0FBQ1Q7QUFtRE8sTUFBTSxzQkFBc0IsQ0FBQyxvQkFBdUM7QUFDekUsUUFBTSxrQkFBa0IsQ0FBQyxlQUFlLGVBQWUsb0JBQW9CO0FBRTNFLE1BQUcsQ0FBQyxpQkFBaUI7QUFDbkIsV0FBTztBQUFBLEVBQ1Q7QUFFQSxTQUFPLENBQUMsZUFBZTtBQUN6QjtBQUVBLE1BQU0sMkJBQTJCLE1BQWdCO0FBQy9DLFFBQU0sY0FBYyxTQUFTLDRCQUE0QjtBQUFBLElBQ3ZELEtBQUssUUFBUSxJQUFJO0FBQUEsSUFDakIsUUFBUSxDQUFDLHNCQUFzQixjQUFjLGFBQWEsZUFBZSxhQUFhO0FBQUEsRUFDeEYsQ0FBQztBQUVELFFBQU0sWUFBWSxTQUFTLG9DQUFvQztBQUFBLElBQzdELEtBQUssUUFBUSxJQUFJO0FBQUEsSUFDakIsUUFBUSxDQUFDLHNCQUFzQixjQUFjLFdB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