This repository has been archived by the owner on Jun 13, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 4
/
knapsack-pro-core.ts
154 lines (132 loc) · 5.5 KB
/
knapsack-pro-core.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
import { KnapsackProAPI } from './knapsack-pro-api';
import { QueueApiResponseCodes } from './api-response-codes';
import { KnapsackProEnvConfig } from './config';
import { KnapsackProLogger } from './knapsack-pro-logger';
import { FallbackTestDistributor } from './fallback-test-distributor';
import { TestFilesFinder } from './test-files-finder';
import { TestFile } from './models';
import {
onQueueFailureType,
onQueueSuccessType,
testFilesToExecuteType,
} from './types';
import * as Urls from './urls';
export class KnapsackProCore {
private knapsackProAPI: KnapsackProAPI;
private knapsackProLogger: KnapsackProLogger;
// test files with recorded time execution
private recordedTestFiles: TestFile[];
// list of test files in whole user's test suite
private allTestFiles: TestFile[];
private isTestSuiteGreen: boolean;
constructor(
clientName: string,
clientVersion: string,
testFilesToExecute: testFilesToExecuteType,
) {
this.recordedTestFiles = [];
this.allTestFiles =
TestFilesFinder.testFilesFromSourceFile() ?? testFilesToExecute();
this.knapsackProAPI = new KnapsackProAPI(clientName, clientVersion);
this.knapsackProLogger = new KnapsackProLogger();
this.isTestSuiteGreen = true;
}
public runQueueMode(
onSuccess: onQueueSuccessType,
onFailure: onQueueFailureType,
) {
this.fetchTestsFromQueue(true, true, onSuccess, onFailure);
}
private fetchTestsFromQueue(
// eslint-disable-next-line default-param-last
initializeQueue = false,
// eslint-disable-next-line default-param-last
attemptConnectToQueue = false,
onSuccess: onQueueSuccessType,
onFailure: onQueueFailureType,
) {
this.knapsackProAPI
.fetchTestsFromQueue(
this.allTestFiles,
initializeQueue,
attemptConnectToQueue,
)
.then((response) => {
const apiCode: QueueApiResponseCodes = 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);
this.fetchTestsFromQueue(false, false, onSuccess, onFailure);
},
);
})
.catch((error) => {
if (this.knapsackProAPI.isExpectedErrorStatus(error)) {
// when API returned expected error then CI node should fail
// this should prevent from running tests in Fallback Mode
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);
this.finishQueueMode();
});
});
}
private updateRecordedTestFiles(
recordedTestFiles: TestFile[],
isTestSuiteGreen: boolean,
) {
this.recordedTestFiles = this.recordedTestFiles.concat(recordedTestFiles);
this.isTestSuiteGreen = this.isTestSuiteGreen && isTestSuiteGreen;
}
private finishQueueMode() {
this.createBuildSubset(this.recordedTestFiles);
process.exitCode = this.isTestSuiteGreen ? 0 : 1;
}
// saves recorded timing for tests executed on single CI node
private createBuildSubset(testFiles: TestFile[]) {
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
this.knapsackProAPI.createBuildSubset(testFiles).catch((error) => {
this.knapsackProLogger.error(
'Could not save recorded timing of tests due to failed request to Knapsack Pro API.',
);
});
}
}