UNPKG

@flxbl-io/sfp

Version:

sfp is a CLI tool to help you manage your Salesforce projects in an artifact centric model

542 lines 57.3 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ProgressReporter = void 0; const fs = require('fs-extra'); const path = require("path"); const TestOptions_1 = require("./TestOptions"); const IndividualClassCoverage_1 = __importDefault(require("../apex/coverage/IndividualClassCoverage")); const TestReportDisplayer_1 = require("./TestReportDisplayer"); const PackageTestCoverage_1 = __importDefault(require("../package/coverage/PackageTestCoverage")); const sfp_logger_1 = __importStar(require("@flxbl-io/sfp-logger")); const SFPStatsSender_1 = __importDefault(require("../stats/SFPStatsSender")); const core_1 = require("@salesforce/core"); const apex_node_1 = require("@salesforce/apex-node"); const JSONReporter_1 = require("./JSONReporter"); const kit_1 = require("@salesforce/kit"); const ClearCodeCoverage_1 = __importDefault(require("./ClearCodeCoverage")); const lodash_1 = __importDefault(require("lodash")); const retry = require('async-retry'); const dedent_1 = __importDefault(require("dedent")); class TriggerApexTests { constructor(target_org, testOptions, coverageOptions, project_directory, fileLogger) { this.target_org = target_org; this.testOptions = testOptions; this.coverageOptions = coverageOptions; this.project_directory = project_directory; this.fileLogger = fileLogger; this.cancellationTokenSource = new apex_node_1.CancellationTokenSource(); } async exec() { let org = await core_1.Org.create({ aliasOrUsername: this.target_org }); this.conn = org.getConnection(); // graceful shutdown const exitHandler = async () => { await this.cancellationTokenSource.asyncCancel(); process.exit(); }; process.on('SIGINT', exitHandler); process.on('SIGTERM', exitHandler); let startTime = Date.now(); let testExecutionResult = false; let testsRan; let commandTime; try { const testService = new apex_node_1.TestService(this.conn); //Clear Code Coverage before triggering tests try { let clearCodeCoverage = new ClearCodeCoverage_1.default(org, this.fileLogger); await clearCodeCoverage.clear(); } catch (error) { sfp_logger_1.default.log(`Ignoring error in clearing code coverage attributed to ${error}.`, sfp_logger_1.LoggerLevel.DEBUG, this.fileLogger); } //Translate Tests to test levels used by apex-node let translatedTestLevel; //Fetch tests passed in the testOptions let tests = null; let suites = null; let isCoverageToBeFetched = this.coverageOptions.isIndividualClassCoverageToBeValidated || this.coverageOptions.isPackageCoverageToBeValidated; //Translate Test Option ({ translatedTestLevel, tests, suites } = await this.translateTestOptionToAPIVars(this.testOptions)); //Trigger tests asynchronously let testRunResult; try { testRunResult = (await this.triggerTestAsynchronously(testService, translatedTestLevel, isCoverageToBeFetched, tests, suites)); } catch (error) { if (error.message.includes(`Cannot read properties of undefined (reading 'Status')`)) { return { result: false, id: null, message: (0, dedent_1.default) `Unable to obtain status of test run, Did you cancel the test run? This error happens when the test run is cancelled and the status is not available. Please retry`, }; } return { result: false, id: null, message: error.message, }; } //Fetch Test Results let testResult = await retry(async (bail) => { return await testService.reportAsyncResults(testRunResult.summary.testRunId, isCoverageToBeFetched, this.cancellationTokenSource.token); }, { retries: 2, minTimeout: 3000 }); testResult = this.fixBadNamespaceClassFullNames(testResult); //Collect Failed Tests only if Parallel testResult = await this.triggerSecondRunInSerialForParallelFailedTests(testResult, testService, translatedTestLevel, isCoverageToBeFetched); //Filter testResult for duplicate test listing testResult = this.removeDuplicateTestListing(testResult); //Write Test Results to file let jsonOutput = undefined; try { jsonOutput = this.writeTestOutput(testResult); } catch (error) { sfp_logger_1.default.log(`Unable to write test results to file due to ${error}`, sfp_logger_1.LoggerLevel.DEBUG, this.fileLogger); return { result: false, id: testResult.summary.testRunId, message: (0, dedent_1.default) `Unable to fetch test execution results, Please check the results in the org by using the URL below ${this.conn.instanceUrl}/lightning/setup/ApexTestHistory/home Please try the test execution again`, }; } //Print tests result to screen let testReportDisplayer = new TestReportDisplayer_1.TestReportDisplayer(jsonOutput, this.testOptions, this.fileLogger); testReportDisplayer.printTestResults(); commandTime = testResult.summary.commandTimeInMs; if (testResult.summary.outcome == 'Failed') { testExecutionResult = false; return { result: false, id: testResult.summary.testRunId, message: 'Test Execution failed', }; } else { if (isCoverageToBeFetched) { let coverageResults = await this.validateForApexCoverage(jsonOutput.coverage.coverage); testReportDisplayer.printCoverageReport(this.coverageOptions.coverageThreshold, coverageResults.classesCovered, coverageResults.classesWithInvalidCoverage); testsRan = testResult.summary.testsRan; testReportDisplayer.printTestSummary(coverageResults.packageTestCoverage); testExecutionResult = coverageResults.result; SFPStatsSender_1.default.logGauge('apextest.testcoverage', coverageResults.packageTestCoverage, { package: this.testOptions instanceof TestOptions_1.RunAllTestsInPackageOptions ? this.testOptions.sfppackage.packageName : null, }); return { result: coverageResults.result, id: testResult.summary.testRunId, message: coverageResults.message, }; } else { testExecutionResult = true; SFPStatsSender_1.default.logGauge('apextest.testcoverage', Number.parseInt(testResult.summary.testRunCoverage), { package: this.testOptions instanceof TestOptions_1.RunAllTestsInPackageOptions ? this.testOptions.sfppackage.packageName : null, }); return { result: true, id: testResult.summary.testRunId, message: `Test execution succesfully completed`, }; } } } finally { this.reportMetrics(this.testOptions, { targetOrg: this.target_org, startTime, testExecutionResult, testsRan, commandTime, }); } } async translateTestOptionToAPIVars(testOptions) { let translatedTestLevel; let tests; let suites; if (testOptions instanceof TestOptions_1.RunAllTestsInPackageOptions) { ({ translatedTestLevel, tests } = await this.getTranslatedOptionsForAllTestInPackageOptions(testOptions)); } else if (testOptions instanceof TestOptions_1.RunSpecifiedTestsOption) { ({ translatedTestLevel, tests } = await this.getTranslatedOptionsForSpecifiedTests(testOptions)); } else if (testOptions instanceof TestOptions_1.RunApexTestSuitesOption) { translatedTestLevel = "RunSpecifiedTests" /* TestLevel.RunSpecifiedTests */; suites = testOptions.suiteNames; sfp_logger_1.default.log(`Test Suites to be executed: ${(0, sfp_logger_1.COLOR_KEY_MESSAGE)(suites)}`, sfp_logger_1.LoggerLevel.INFO, this.fileLogger); } else if (testOptions instanceof TestOptions_1.RunLocalTests) { translatedTestLevel = "RunLocalTests" /* TestLevel.RunLocalTests */; sfp_logger_1.default.log(`Triggering all ${(0, sfp_logger_1.COLOR_KEY_MESSAGE)(`local tests`)}in the org`, sfp_logger_1.LoggerLevel.INFO, this.fileLogger); } else if (testOptions instanceof TestOptions_1.RunAllTestsInOrg) { sfp_logger_1.default.log(`Triggering all ${(0, sfp_logger_1.COLOR_KEY_MESSAGE)(`all tests`)}in the org`, sfp_logger_1.LoggerLevel.INFO, this.fileLogger); translatedTestLevel = "RunAllTestsInOrg" /* TestLevel.RunAllTestsInOrg */; } return { translatedTestLevel, tests, suites }; } removeDuplicateTestListing(testResult) { let modifiedTestResult = lodash_1.default.cloneDeep(testResult); let toEliminateIndices = []; for (let index = 0; index < modifiedTestResult.tests.length; index++) { let idx = index; let duplicateIndices = [index]; while (idx != -1) { idx = lodash_1.default.findIndex(modifiedTestResult.tests, (elem) => { return elem.methodName == modifiedTestResult.tests[index].methodName && elem.apexClass.name == modifiedTestResult.tests[index].apexClass.name; }, idx + 1); if (idx != -1) duplicateIndices.push(idx); } if (duplicateIndices.length > 1) { for (const idx of duplicateIndices) { if (modifiedTestResult.tests[idx].outcome != 'Pass') toEliminateIndices.push(idx); } } } modifiedTestResult.tests = modifiedTestResult.tests.filter(function (value, index, arr) { return !toEliminateIndices.includes(index); }); if (toEliminateIndices.length > 0) modifiedTestResult = this.combineTestResult(modifiedTestResult); return modifiedTestResult; } async getTranslatedOptionsForSpecifiedTests(testOptions) { let translatedTestLevel = "RunSpecifiedTests" /* TestLevel.RunSpecifiedTests */; let tests = testOptions.specifiedTests; sfp_logger_1.default.log(`Tests to be executed: ${(0, sfp_logger_1.COLOR_KEY_MESSAGE)(tests)}`, sfp_logger_1.LoggerLevel.INFO, this.fileLogger); sfp_logger_1.default.log(`Test Mode: ${(0, sfp_logger_1.COLOR_KEY_MESSAGE)(this.testOptions.synchronous == true ? 'serial' : 'parallel')}`, sfp_logger_1.LoggerLevel.INFO, this.fileLogger); //Toggle to serial await this.toggleParallelApexTesting(this.conn, this.fileLogger, this.testOptions.synchronous == true ? true : false); return { translatedTestLevel, tests }; } async getTranslatedOptionsForAllTestInPackageOptions(testOptions) { sfp_logger_1.default.log(`Test Mode Descriptor in Package 'testSynchronous': ${testOptions.sfppackage.packageDescriptor.testSynchronous ? testOptions.sfppackage.packageDescriptor.testSynchronous : false}`, sfp_logger_1.LoggerLevel.TRACE, this.fileLogger); sfp_logger_1.default.log(`Test Mode: ${(0, sfp_logger_1.COLOR_KEY_MESSAGE)(testOptions.synchronous == true ? 'serial' : 'parallel')}`, sfp_logger_1.LoggerLevel.INFO, this.fileLogger); await this.toggleParallelApexTesting(this.conn, this.fileLogger, testOptions.synchronous == true ? true : false); let translatedTestLevel = "RunSpecifiedTests" /* TestLevel.RunSpecifiedTests */; let tests = testOptions.specifiedTests; sfp_logger_1.default.log(`Tests to be executed: ${(0, sfp_logger_1.COLOR_KEY_MESSAGE)(tests)}`, sfp_logger_1.LoggerLevel.INFO, this.fileLogger); return { translatedTestLevel, tests }; } async triggerSecondRunInSerialForParallelFailedTests(testResult, testService, translatedTestLevel, isCoverageToBeFetched) { let modifiedTestResult = lodash_1.default.cloneDeep(testResult); if (!this.testOptions.synchronous) { let parallelFailedTestClasses = []; let testClassesThatDonotContributedCoverage = []; let testToBeTriggered = []; for (const test of modifiedTestResult.tests) { if (test.outcome == "Fail" /* ApexTestResultOutcome.Fail */) { //Check for messages if (test.message.includes(`Your request exceeded the time limit for processing`) || test.message.includes(`UNABLE_TO_LOCK_ROW`) || test.message.includes(`Internal Salesforce Error`) || test.message.includes(`LIMIT_EXCEEDED`) || test.message.includes(`Too many concurrent Apex compilations during resource mitigation`)) { if (!testToBeTriggered.includes(test.apexClass.fullName)) { parallelFailedTestClasses.push(test.apexClass.fullName); testToBeTriggered.push(test.apexClass.fullName); } } } if (test.outcome == "Pass" /* ApexTestResultOutcome.Pass */) { if (!test.perClassCoverage && (this.coverageOptions.isPackageCoverageToBeValidated || this.coverageOptions.isIndividualClassCoverageToBeValidated)) { if (!testToBeTriggered.includes(test.apexClass.fullName)) { testClassesThatDonotContributedCoverage.push(test.apexClass.fullName); if (!testToBeTriggered.includes(test.apexClass.fullName)) testToBeTriggered.push(test.apexClass.fullName); } } } } if (parallelFailedTestClasses.length > 0) { sfp_logger_1.default.log(`Failed Tests while triggered in parallel: ${(0, sfp_logger_1.COLOR_KEY_MESSAGE)(parallelFailedTestClasses.toString())}`, sfp_logger_1.LoggerLevel.INFO, this.fileLogger); } if (testClassesThatDonotContributedCoverage.length > 0) { sfp_logger_1.default.log(`Test Classes that were not able to contribute coverage: ${(0, sfp_logger_1.COLOR_KEY_MESSAGE)(testClassesThatDonotContributedCoverage.toString())}`, sfp_logger_1.LoggerLevel.INFO, this.fileLogger); } if (testToBeTriggered.length > 0) { sfp_logger_1.default.log(`Triggering tests synchronously: ${(0, sfp_logger_1.COLOR_KEY_MESSAGE)(testToBeTriggered.toString())}`, sfp_logger_1.LoggerLevel.INFO, this.fileLogger); //Trigger Second Test Run //Convert to sequential await this.toggleParallelApexTesting(this.conn, this.fileLogger, true); //Trigger tests asynchronously let secondRuntestRunResult; secondRuntestRunResult = await retry(async (bail) => { return (await this.triggerTestAsynchronously(testService, translatedTestLevel, isCoverageToBeFetched, testToBeTriggered.toString(), null)); }, { retries: 2, minTimeout: 3000 }); secondRuntestRunResult = this.fixBadNamespaceClassFullNames(secondRuntestRunResult); //Fetch Test Results const secondTestResult = this.fixBadNamespaceClassFullNames(await testService.reportAsyncResults(secondRuntestRunResult.summary.testRunId, true, this.cancellationTokenSource.token)); this.writeTestOutput(secondTestResult); //Merge original test results with second run const mergedTestResults = modifiedTestResult.tests; for (const testObject of secondTestResult.tests) { const index = mergedTestResults.findIndex((test) => test.fullName === testObject.fullName); if (index !== -1) { mergedTestResults[index] = testObject; } else { mergedTestResults.push(testObject); } } modifiedTestResult.tests = mergedTestResults; //Merge original code coverage with second run if (isCoverageToBeFetched) { const mergedCodecoverage = modifiedTestResult.codecoverage; for (const codeCoverageObject of secondTestResult.codecoverage) { const index = mergedCodecoverage.findIndex((codeCoverage) => codeCoverage.name === codeCoverageObject.name); if (index !== -1) { mergedCodecoverage[index] = codeCoverageObject; } else { mergedCodecoverage.push(codeCoverageObject); } } modifiedTestResult.codecoverage = mergedCodecoverage; } //Now redo the math modifiedTestResult = this.combineTestResult(modifiedTestResult, secondRuntestRunResult); } } return modifiedTestResult; } fixBadNamespaceClassFullNames(testResult) { let modifiedTestResult = lodash_1.default.cloneDeep(testResult); try { modifiedTestResult.tests = modifiedTestResult.tests.map((test) => { return { ...test, ...{ fullName: test.fullName?.replace('__', '.'), apexClass: { ...test.apexClass, ...{ fullName: test.apexClass?.fullName?.replace('__', '.'), }, }, }, }; }); } catch (error) { sfp_logger_1.default.log(`Unable to fix bad namespace class full names due to ${error}`, sfp_logger_1.LoggerLevel.DEBUG, this.fileLogger); modifiedTestResult = lodash_1.default.cloneDeep(testResult); } return modifiedTestResult; } combineTestResult(testResult, testResultSecondRun) { testResult.summary.failing = 0; testResult.summary.passing = 0; testResult.summary.skipped = 0; for (const test of testResult.tests) { if (test.outcome === "Pass" /* ApexTestResultOutcome.Pass */) testResult.summary.passing++; else if (test.outcome === "Fail" /* ApexTestResultOutcome.Fail */) testResult.summary.failing++; else if (test.outcome === "Skip" /* ApexTestResultOutcome.Skip */) testResult.summary.skipped++; } if (testResult.summary.failing > 0) testResult.summary.outcome = 'Failed'; else testResult.summary.outcome = 'Passed'; testResult.summary.passRate = (testResult.summary.passing / testResult.summary.testsRan) * 100 + '%'; testResult.summary.failRate = (testResult.summary.failing / testResult.summary.testsRan) * 100 + '%'; testResult.summary.commandTimeInMs = testResult.summary.commandTimeInMs + testResultSecondRun?.summary.commandTimeInMs; testResult.summary.testExecutionTimeInMs = testResult.summary.testExecutionTimeInMs + testResultSecondRun?.summary.testExecutionTimeInMs; testResult.summary.testTotalTimeInMs = testResult.summary.testTotalTimeInMs + testResultSecondRun?.summary.testTotalTimeInMs; delete testResult.summary.testRunCoverage; delete testResult.summary.orgWideCoverage; delete testResult.summary.totalLines; delete testResult.summary.coveredLines; if (testResultSecondRun) testResult.summary.testRunId = testResult.summary.testRunId.concat('_', testResultSecondRun.summary.testRunId); return testResult; } /** * Trigger tests asynchronously * @param {TestService} testService * @param {TestLevel} testLevel * @param {string} tests? * @param {string} suites? */ async triggerTestAsynchronously(testService, testLevel, isCoverageToBeFetched, tests, suites) { const payload = await testService.buildAsyncPayload(testLevel, null, tests, suites); let result = await testService.runTestAsynchronous(payload, isCoverageToBeFetched, false, new ProgressReporter(this.fileLogger), this.cancellationTokenSource.token); if (this.cancellationTokenSource.token.isCancellationRequested) { throw new Error(`A previous run is being cancelled.. Please try after some time`); } return result; } async validateForApexCoverage(coverageReport) { if (this.testOptions instanceof TestOptions_1.RunAllTestsInPackageOptions) { let packageTestCoverage = new PackageTestCoverage_1.default(this.testOptions.sfppackage, coverageReport, this.fileLogger, this.conn); return packageTestCoverage.validateTestCoverage(this.coverageOptions.coverageThreshold); } else { if (this.coverageOptions.isIndividualClassCoverageToBeValidated) { let coverageValidator = new IndividualClassCoverage_1.default(coverageReport, this.fileLogger); return coverageValidator.validateIndividualClassCoverage(coverageValidator.getIndividualClassCoverage(this.coverageOptions.classesToBeValidated), this.coverageOptions.coverageThreshold); } else { let coverageValidator = new IndividualClassCoverage_1.default(coverageReport, this.fileLogger); return coverageValidator.validateIndividualClassCoverage(coverageValidator.getIndividualClassCoverage()); } } } writeJUnit(testResult) { sfp_logger_1.default.log(`Junit Report file available at ${path.join(this.testOptions.outputdir, `test-result-${testResult.summary.testRunId}-junit.xml`)}`); let reportAsJUnitReport = new apex_node_1.JUnitReporter().format(testResult); fs.writeFileSync(path.join(this.testOptions.outputdir, `test-result-${testResult.summary.testRunId}-junit.xml`), reportAsJUnitReport); } writeTestOutput(testResult) { const jsonOutput = this.formatResultInJson(testResult); //write output files fs.ensureDirSync(this.testOptions.outputdir); //Write files fs.writeJSONSync(path.join(this.testOptions.outputdir, `test-result-${testResult.summary.testRunId}.json`), testResult, { spaces: 4 }); if (jsonOutput.coverage) fs.writeJSONSync(path.join(this.testOptions.outputdir, `test-result-${testResult.summary.testRunId}-coverage.json`), jsonOutput.coverage?.coverage, { spaces: 4 }); //Write Junit Result no matter what this.writeJUnit(testResult); return jsonOutput; } formatResultInJson(result) { try { const reporter = new JSONReporter_1.JsonReporter(); return reporter.format(result); } catch (error) { return null; } } //Enable Synchronus Compile on Deploy async toggleParallelApexTesting(conn, logger, toEnable) { try { sfp_logger_1.default.log(`Set enableDisableParallelApexTesting:${toEnable}`, sfp_logger_1.LoggerLevel.TRACE, logger); let apexSettingMetadata = { fullName: 'ApexSettings', enableDisableParallelApexTesting: toEnable }; let result = await conn.metadata.upsert('ApexSettings', apexSettingMetadata); if (result.success) { sfp_logger_1.default.log(`Successfully updated apex testing setting`, sfp_logger_1.LoggerLevel.INFO, logger); } } catch (error) { sfp_logger_1.default.log(`Skipping toggling of enableDisableParallelApexTesting due to ${error}..`, sfp_logger_1.LoggerLevel.INFO, logger); } } reportMetrics(testOptions, testMetrics) { let elapsedTime = Date.now() - testMetrics.startTime; if (testMetrics.testExecutionResult) SFPStatsSender_1.default.logGauge('apextest.tests.ran', testMetrics.testsRan, { test_result: String(testMetrics.testExecutionResult), package: testOptions instanceof TestOptions_1.RunAllTestsInPackageOptions ? testOptions.sfppackage.packageName : null, type: testOptions.testLevel, target_org: testMetrics.targetOrg, }); SFPStatsSender_1.default.logGauge('apextest.testtotal.time', elapsedTime, { test_result: String(testMetrics.testExecutionResult), package: testOptions instanceof TestOptions_1.RunAllTestsInPackageOptions ? testOptions.sfppackage.packageName : null, type: testOptions['testlevel'], target_org: testMetrics.targetOrg, }); if (testMetrics.commandTime) SFPStatsSender_1.default.logGauge('apextest.command.time', testMetrics.commandTime, { test_result: String(testMetrics.testExecutionResult), package: testOptions instanceof TestOptions_1.RunAllTestsInPackageOptions ? testOptions.sfppackage.packageName : null, type: testOptions.testLevel, target_org: testMetrics.targetOrg, }); SFPStatsSender_1.default.logCount('apextests.triggered', { test_result: String(testMetrics.testExecutionResult), package: testOptions instanceof TestOptions_1.RunAllTestsInPackageOptions ? testOptions.sfppackage.packageName : null, type: testOptions.testLevel, target_org: testMetrics.targetOrg, }); } } exports.default = TriggerApexTests; class ProgressReporter { constructor(logger) { this.logger = logger; this.lastExecutedTime = Date.now(); } report(value) { try { let count = {}; //Limit printing an update to 30 seconds if (Date.now() - this.lastExecutedTime > kit_1.Duration.seconds(30).milliseconds) { if (value.type == 'TestQueueProgress') { for (const elem of value.value.records) { if (elem?.Status) { if (!count[elem.Status]) { count[elem.Status] = 1; } else count[elem.Status]++; } } let statusString = ''; //Compute total let total = 0; for (const [key, value] of Object.entries(count)) { total += value; } statusString = `Completed:${count['Completed'] ? count['Completed'] : 0}/${total} Queued(${count['Queued'] ? count['Queued'] : 0}) Failed(${(0, sfp_logger_1.COLOR_ERROR)(count['Failed'] ? count['Failed'] : 0)}) `; sfp_logger_1.default.log(`Test Status: ` + (0, sfp_logger_1.COLOR_KEY_MESSAGE)(statusString), sfp_logger_1.LoggerLevel.INFO, this.logger); this.lastExecutedTime = Date.now(); } } } catch (error) { console.log(`Bummer`); } } } exports.ProgressReporter = ProgressReporter; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiVHJpZ2dlckFwZXhUZXN0cy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9jb3JlL2FwZXh0ZXN0L1RyaWdnZXJBcGV4VGVzdHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSxNQUFNLEVBQUUsR0FBRyxPQUFPLENBQUMsVUFBVSxDQUFDLENBQUM7QUFDL0IsNkJBQThCO0FBQzlCLCtDQU91QjtBQUN2Qix1R0FBb0c7QUFDcEcsK0RBQTREO0FBQzVELGtHQUEwRTtBQUMxRSxtRUFBc0c7QUFDdEcsNkVBQXFEO0FBQ3JELDJDQUFtRDtBQUNuRCxxREFXK0I7QUFDL0IsaURBQTZEO0FBQzdELHlDQUEyQztBQUMzQyw0RUFBb0Q7QUFDcEQsb0RBQXVCO0FBQ3ZCLE1BQU0sS0FBSyxHQUFHLE9BQU8sQ0FBQyxhQUFhLENBQUMsQ0FBQztBQUNyQyxvREFBNEI7QUFFNUIsTUFBcUIsZ0JBQWdCO0lBSWpDLFlBQ1ksVUFBa0IsRUFDbEIsV0FBd0IsRUFDeEIsZUFBZ0MsRUFDaEMsaUJBQXlCLEVBQ3pCLFVBQWdCO1FBSmhCLGVBQVUsR0FBVixVQUFVLENBQVE7UUFDbEIsZ0JBQVcsR0FBWCxXQUFXLENBQWE7UUFDeEIsb0JBQWUsR0FBZixlQUFlLENBQWlCO1FBQ2hDLHNCQUFpQixHQUFqQixpQkFBaUIsQ0FBUTtRQUN6QixlQUFVLEdBQVYsVUFBVSxDQUFNO1FBUGxCLDRCQUF1QixHQUFHLElBQUksbUNBQXVCLEVBQUUsQ0FBQztJQVEvRCxDQUFDO0lBRUcsS0FBSyxDQUFDLElBQUk7UUFLYixJQUFJLEdBQUcsR0FBRyxNQUFNLFVBQUcsQ0FBQyxNQUFNLENBQUMsRUFBRSxlQUFlLEVBQUUsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDLENBQUM7UUFDakUsSUFBSSxDQUFDLElBQUksR0FBRyxHQUFHLENBQUMsYUFBYSxFQUFFLENBQUM7UUFFaEMsb0JBQW9CO1FBQ3BCLE1B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