UNPKG

@knapsack-pro/core

Version:

Knapsack Pro Core library splits tests across CI nodes and makes sure that tests will run in optimal time on each CI node. This library gives core features like communication with KnapsackPro.com API. This library is a dependency for other projects specif

91 lines (90 loc) 5.17 kB
import { KnapsackProAPI } from './knapsack-pro-api.js'; import { QueueApiResponseCodes } from './api-response-codes.js'; import { KnapsackProEnvConfig } from './config/index.js'; import { KnapsackProLogger } from './knapsack-pro-logger.js'; import { FallbackTestDistributor } from './fallback-test-distributor.js'; import { TestFilesFinder } from './test-files-finder.js'; import * as Urls from './urls.js'; export class KnapsackProCore { knapsackProAPI; knapsackProLogger; recordedTestFiles; allTestFiles; isTestSuiteGreen; constructor(clientName, clientVersion, testFilesToExecute) { this.recordedTestFiles = []; this.allTestFiles = TestFilesFinder.testFilesFromSourceFile() ?? testFilesToExecute(); this.knapsackProAPI = new KnapsackProAPI(clientName, clientVersion); this.knapsackProLogger = new KnapsackProLogger(); this.isTestSuiteGreen = true; } runQueueMode(onSuccess, onFailure) { this.fetchTestsFromQueue(true, true, onSuccess, onFailure); } fetchTestsFromQueue(initializeQueue = false, attemptConnectToQueue = false, onSuccess, onFailure) { this.knapsackProAPI .fetchTestsFromQueue(this.allTestFiles, initializeQueue, attemptConnectToQueue) .then((response) => { const apiCode = response.data.code; if (apiCode === QueueApiResponseCodes.AttemptConnectToQueueFailed) { this.fetchTestsFromQueue(true, false, onSuccess, onFailure); return; } const queueTestFiles = response.data.test_files; const isQueueEmpty = queueTestFiles.length === 0; if (isQueueEmpty) { this.finishQueueMode(); return; } onSuccess(queueTestFiles).then(({ recordedTestFiles, isTestSuiteGreen }) => { this.updateRecordedTestFiles(recordedTestFiles, isTestSuiteGreen, queueTestFiles); this.fetchTestsFromQueue(false, false, onSuccess, onFailure); }); }) .catch((error) => { if (this.knapsackProAPI.isExpectedErrorStatus(error)) { process.exitCode = 1; throw new Error('Knapsack Pro API returned an error. See the above logs.'); } onFailure(error); if (KnapsackProEnvConfig.ciNodeRetryCount > 0) { throw new Error(`No connection to Knapsack Pro API to determine the set of tests that should run on the retried CI node. Please retry the CI node to reconnect with the API or create a new commit to start another CI build that could run tests in Fallback Mode in case of persisting connection issues with the API. Learn more ${Urls.QUEUE_MODE_CONNECTION_ERROR_AND_POSITIVE_RETRY_COUNT}`); } this.knapsackProLogger.warn('Fallback Mode has started. We could not connect to Knapsack Pro API. Your tests will be executed based on test file names.\n\nIf other CI nodes were able to connect to Knapsack Pro API then you may notice that some of the test files were executed twice across CI nodes. Fallback Mode guarantees each of test files is run at least once as a part of CI build.'); const fallbackTestDistributor = new FallbackTestDistributor(this.allTestFiles, this.recordedTestFiles); const testFiles = fallbackTestDistributor.testFilesForCiNode(); const executedTestFiles = KnapsackProLogger.objectInspect(this.recordedTestFiles); this.knapsackProLogger.debug(`Test files already executed:\n${executedTestFiles}`); const inspectedTestFiles = KnapsackProLogger.objectInspect(testFiles); this.knapsackProLogger.debug(`Test files to be run in Fallback Mode:\n${inspectedTestFiles}`); onSuccess(testFiles).then(({ recordedTestFiles, isTestSuiteGreen }) => { this.updateRecordedTestFiles(recordedTestFiles, isTestSuiteGreen, testFiles); this.finishQueueMode(); }); }); } updateRecordedTestFiles(recordedTestFiles, isTestSuiteGreen, scheduledTestPaths) { this.recordedTestFiles = updateRecordedTestFiles(this.recordedTestFiles, recordedTestFiles, scheduledTestPaths); this.isTestSuiteGreen = this.isTestSuiteGreen && isTestSuiteGreen; } finishQueueMode() { this.createBuildSubset(this.recordedTestFiles); process.exitCode = this.isTestSuiteGreen ? 0 : 1; } createBuildSubset(testFiles) { this.knapsackProAPI.createBuildSubset(testFiles).catch((error) => { this.knapsackProLogger.error('Could not save recorded timing of tests due to failed request to Knapsack Pro API.'); }); } } export function updateRecordedTestFiles(recordedTestFiles, newRecordedTestFiles, scheduledTestFiles) { const map = new Map(); scheduledTestFiles.forEach((testFile) => { map.set(testFile.path, { path: testFile.path, time_execution: 0 }); }); newRecordedTestFiles.forEach((testFile) => { map.set(testFile.path, testFile); }); return recordedTestFiles.concat(Array.from(map.values())); }