testit-js-commons 4.1.1 → 4.1.2-TMS-5.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -168,7 +168,8 @@ class ConfigComposer {
168
168
  }
169
169
  if (config.adapterMode == 2) {
170
170
  if (config.testRunId.match('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$') !== null) {
171
- logger_1.default.error(`Adapter works in mode 2. Config should not contains test run id.`);
171
+ logger_1.default.warn(`Adapter works in mode 2. Config should not contains test run id.`);
172
+ config.testRunId = "";
172
173
  }
173
174
  }
174
175
  else if (config.adapterMode == 1) {
@@ -1 +1,2 @@
1
+ export declare function isConflictError(err: unknown): boolean;
1
2
  export declare function handleHttpError(err: any, message?: string): void;
@@ -3,8 +3,16 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.isConflictError = isConflictError;
6
7
  exports.handleHttpError = handleHttpError;
8
+ const utils_1 = require("../../common/utils");
7
9
  const logger_1 = __importDefault(require("../../logger"));
10
+ function isConflictError(err) {
11
+ var _a;
12
+ const e = (0, utils_1.unwrapHttpError)(err);
13
+ const body = (_a = err === null || err === void 0 ? void 0 : err.body) !== null && _a !== void 0 ? _a : e === null || e === void 0 ? void 0 : e.body;
14
+ return (body === null || body === void 0 ? void 0 : body.type) === "ConflictException" || (0, utils_1.getHttpStatus)(e !== null && e !== void 0 ? e : {}) === 409;
15
+ }
8
16
  function handleHttpError(err, message = "") {
9
17
  logger_1.default.error(`HttpError ${err.statusCode}: ${message}. Error body: \n`, err.body);
10
18
  }
@@ -64,11 +64,22 @@ class AutotestsService extends common_1.BaseService {
64
64
  const autotestPost = this._converter.toOriginAutotest(autotest);
65
65
  (0, common_1.escapeHtmlInObject)(autotestPost);
66
66
  logger_1.default.debug("[autotests] createAutoTest", { externalId: autotest.externalId, name: autotest.name });
67
- return yield (0, common_1.withHttpRetry)(() => this._client
68
- .createAutoTest({ autoTestCreateApiModel: autotestPost }), { label: "createAutoTest" })
69
- .then(() => logger_1.default.log(`Create autotest "${autotest.name}".`))
67
+ try {
68
+ yield (0, common_1.withHttpRetry)(() => this._client.createAutoTest({ autoTestCreateApiModel: autotestPost }), { label: "createAutoTest" });
69
+ logger_1.default.log(`Create autotest "${autotest.name}".`);
70
+ }
71
+ catch (err) {
72
+ if ((0, autotests_handler_1.isConflictError)(err)) {
73
+ logger_1.default.debug("[autotests] createAutoTest skipped: already exists", {
74
+ externalId: autotest.externalId,
75
+ name: autotest.name,
76
+ });
77
+ return;
78
+ }
70
79
  // @ts-ignore
71
- .catch((err) => (0, autotests_handler_1.handleHttpError)(err, `Failed create autotest "${autotestPost.name}"`));
80
+ (0, autotests_handler_1.handleHttpError)(err, `Failed create autotest "${autotestPost.name}"`);
81
+ throw err;
82
+ }
72
83
  });
73
84
  }
74
85
  updateAutotest(autotest) {
@@ -87,13 +98,19 @@ class AutotestsService extends common_1.BaseService {
87
98
  loadAutotest(autotest, status) {
88
99
  return __awaiter(this, void 0, void 0, function* () {
89
100
  var _a, _b;
90
- const originAutotest = yield this.getAutotestByExternalId(autotest.externalId);
101
+ let originAutotest = yield this.getAutotestByExternalId(autotest.externalId);
91
102
  if (!originAutotest) {
92
103
  logger_1.default.debug("[autotests] loadAutotest → create", { externalId: autotest.externalId, status });
93
104
  yield this.createAutotest(autotest);
94
- return;
105
+ originAutotest = yield this.getAutotestByExternalId(autotest.externalId);
106
+ if (!originAutotest) {
107
+ logger_1.default.log(`Cannot load autotest "${autotest.name}": not found after create`);
108
+ return;
109
+ }
110
+ }
111
+ else {
112
+ logger_1.default.debug("[autotests] loadAutotest → update path", { externalId: autotest.externalId, status });
95
113
  }
96
- logger_1.default.debug("[autotests] loadAutotest → update path", { externalId: autotest.externalId, status });
97
114
  const mergedAutotest = Object.assign(Object.assign({}, autotest), { namespace: (_a = autotest.namespace) !== null && _a !== void 0 ? _a : originAutotest.namespace, classname: (_b = autotest.classname) !== null && _b !== void 0 ? _b : originAutotest.classname });
98
115
  // fix the issue with lowercase status
99
116
  const currentStatus = status.toLowerCase();
@@ -163,6 +180,7 @@ class AutotestsService extends common_1.BaseService {
163
180
  }
164
181
  getAutotestByExternalId(externalId) {
165
182
  return __awaiter(this, void 0, void 0, function* () {
183
+ var _a;
166
184
  const filterModel = {
167
185
  externalIds: [externalId],
168
186
  projectIds: [this.config.projectId],
@@ -177,20 +195,21 @@ class AutotestsService extends common_1.BaseService {
177
195
  filter: filterModel,
178
196
  includes: includesModel,
179
197
  };
180
- return yield this._client
181
- .apiV2AutoTestsSearchPost({ autoTestSearchApiModel: requestModel })
198
+ try {
199
+ const response = yield (0, common_1.withHttpRetry)(() => this._client.apiV2AutoTestsSearchPost({ autoTestSearchApiModel: requestModel }), { label: `searchAutoTest:${externalId}` });
182
200
  // @ts-ignore
183
- .then((response) => {
184
201
  const data = response.body || response;
185
- return data ? data[0] : null;
186
- })
187
- .then((autotest) => {
202
+ const autotest = data ? data[0] : null;
188
203
  return autotest ? this._converter.toLocalAutotest(autotest) : null;
189
- })
190
- .catch((reason) => {
191
- logger_1.default.error(reason);
204
+ }
205
+ catch (reason) {
206
+ logger_1.default.error("[autotests] getAutotestByExternalId failed", {
207
+ externalId,
208
+ code: reason === null || reason === void 0 ? void 0 : reason.code,
209
+ status: (_a = reason === null || reason === void 0 ? void 0 : reason.status) !== null && _a !== void 0 ? _a : reason === null || reason === void 0 ? void 0 : reason.statusCode,
210
+ });
192
211
  return null;
193
- });
212
+ }
194
213
  });
195
214
  }
196
215
  }
@@ -2,8 +2,11 @@ import { TestResultsFilterApiModel } from "testit-api-client";
2
2
  import { AdapterConfig, BaseConverter } from "../../common";
3
3
  export interface ITestResultsConverter {
4
4
  getTestResultsFilterApiModel(): TestResultsFilterApiModel;
5
+ getTestResultsFilterForRun(): TestResultsFilterApiModel;
5
6
  }
6
7
  export declare class TestResultsConverter extends BaseConverter implements ITestResultsConverter {
7
8
  constructor(config: AdapterConfig);
9
+ private buildRunFilter;
8
10
  getTestResultsFilterApiModel(): TestResultsFilterApiModel;
11
+ getTestResultsFilterForRun(): TestResultsFilterApiModel;
9
12
  }
@@ -6,11 +6,11 @@ class TestResultsConverter extends common_1.BaseConverter {
6
6
  constructor(config) {
7
7
  super(config);
8
8
  }
9
- getTestResultsFilterApiModel() {
10
- const model = {
9
+ buildRunFilter(statusTypes) {
10
+ return {
11
11
  testRunIds: [this.config.testRunId],
12
12
  configurationIds: [this.config.configurationId],
13
- statusTypes: ["InProgress"],
13
+ statusTypes,
14
14
  statusCodes: undefined,
15
15
  outcomes: undefined,
16
16
  failureCategories: undefined,
@@ -25,9 +25,14 @@ class TestResultsConverter extends common_1.BaseConverter {
25
25
  duration: undefined,
26
26
  resultReasons: undefined,
27
27
  autoTestTags: undefined,
28
- excludeAutoTestTags: undefined
28
+ excludeAutoTestTags: undefined,
29
29
  };
30
- return model;
30
+ }
31
+ getTestResultsFilterApiModel() {
32
+ return this.buildRunFilter(["InProgress"]);
33
+ }
34
+ getTestResultsFilterForRun() {
35
+ return this.buildRunFilter(undefined);
31
36
  }
32
37
  }
33
38
  exports.TestResultsConverter = TestResultsConverter;
@@ -9,5 +9,7 @@ export declare class TestResultsService extends BaseService implements ITestResu
9
9
  protected _testsLimit: number;
10
10
  constructor(config: AdapterConfig);
11
11
  getExternalIdsForRun(): Promise<string[]>;
12
+ findTestResultIdByExternalId(externalId: string): Promise<string | undefined>;
13
+ updateTestResult(testResultId: string, model: unknown): Promise<void>;
12
14
  private getTestResults;
13
15
  }
@@ -46,6 +46,7 @@ exports.TestResultsService = void 0;
46
46
  // @ts-ignore
47
47
  const TestitApiClient = __importStar(require("testit-api-client"));
48
48
  const common_1 = require("../../common");
49
+ const utils_1 = require("../../common/utils");
49
50
  const testresults_handler_1 = require("./testresults.handler");
50
51
  const testresults_converter_1 = require("./testresults.converter");
51
52
  class TestResultsService extends common_1.BaseService {
@@ -65,7 +66,7 @@ class TestResultsService extends common_1.BaseService {
65
66
  const testResults = yield this.getTestResults(skip, model);
66
67
  if (testResults.length != 0) {
67
68
  externalIds = externalIds.concat(testResults.map((// @ts-ignore
68
- result) => result.autotestExternalId).filter((id) => id !== undefined));
69
+ result) => { var _a, _b; return (_a = result.autotestExternalId) !== null && _a !== void 0 ? _a : (_b = result.autoTest) === null || _b === void 0 ? void 0 : _b.externalId; }).filter((id) => id !== undefined));
69
70
  skip += this._testsLimit;
70
71
  continue;
71
72
  }
@@ -73,6 +74,36 @@ class TestResultsService extends common_1.BaseService {
73
74
  }
74
75
  });
75
76
  }
77
+ findTestResultIdByExternalId(externalId) {
78
+ return __awaiter(this, void 0, void 0, function* () {
79
+ var _a, _b;
80
+ const model = this._converter.getTestResultsFilterForRun();
81
+ let skip = 0;
82
+ while (true) {
83
+ const batch = yield this.getTestResults(skip, model);
84
+ if (batch.length === 0) {
85
+ return undefined;
86
+ }
87
+ for (const row of batch) {
88
+ const rowExternalId = (_a = row.autotestExternalId) !== null && _a !== void 0 ? _a : (_b = row.autoTest) === null || _b === void 0 ? void 0 : _b.externalId;
89
+ if (rowExternalId === externalId && row.id) {
90
+ return row.id;
91
+ }
92
+ }
93
+ skip += this._testsLimit;
94
+ if (batch.length < this._testsLimit) {
95
+ return undefined;
96
+ }
97
+ }
98
+ });
99
+ }
100
+ updateTestResult(testResultId, model) {
101
+ return __awaiter(this, void 0, void 0, function* () {
102
+ yield (0, utils_1.withHttpRetry)(() => this._client.apiV2TestResultsIdPut(testResultId, {
103
+ testResultUpdateV2Request: model,
104
+ }), { label: `apiV2TestResultsIdPut:${testResultId}` });
105
+ });
106
+ }
76
107
  getTestResults(skip, model) {
77
108
  return __awaiter(this, void 0, void 0, function* () {
78
109
  return yield this._client
@@ -1,3 +1,5 @@
1
1
  export interface ITestResultsService {
2
2
  getExternalIdsForRun(): Promise<string[]>;
3
+ findTestResultIdByExternalId(externalId: string): Promise<string | undefined>;
4
+ updateTestResult(testResultId: string, model: unknown): Promise<void>;
3
5
  }
@@ -7,6 +7,7 @@ export interface ITestRunConverter {
7
7
  toLocalTestRun(testRun: TestRunV2ApiResult): TestRunGet;
8
8
  toOriginAutotestResult(autotest: AutotestResult): AutoTestResultsForTestRunModel;
9
9
  toOriginAutotestResultInProgress(autotest: AutotestResult): AutoTestResultsForTestRunModel;
10
+ toOriginTestResultUpdate(autotest: AutotestResult): unknown;
10
11
  }
11
12
  export declare class TestRunConverter extends BaseConverter implements ITestRunConverter {
12
13
  private autotestConverter;
@@ -16,5 +17,6 @@ export declare class TestRunConverter extends BaseConverter implements ITestRunC
16
17
  mapToStatusType(status: Outcome): string;
17
18
  toOriginAutotestResultInProgress(autotest: AutotestResult): AutoTestResultsForTestRunModel;
18
19
  toOriginAutotestResult(autotest: AutotestResult): AutoTestResultsForTestRunModel;
20
+ toOriginTestResultUpdate(autotest: AutotestResult): unknown;
19
21
  toLocalTestRun(testRun: TestRunV2ApiResult): TestRunGet;
20
22
  }
@@ -62,6 +62,26 @@ class TestRunConverter extends common_1.BaseConverter {
62
62
  }
63
63
  return model;
64
64
  }
65
+ toOriginTestResultUpdate(autotest) {
66
+ var _a, _b, _c, _d, _e;
67
+ const model = {
68
+ outcome: this.toOriginOutcome(autotest.outcome),
69
+ statusType: this.mapToStatusType(autotest.outcome),
70
+ statusCode: null,
71
+ links: (_a = autotest.links) === null || _a === void 0 ? void 0 : _a.map((link) => this.toOriginLink(link)),
72
+ stepResults: (_b = autotest.stepResults) === null || _b === void 0 ? void 0 : _b.map((step) => this.toOriginStep(step)),
73
+ setupResults: (_c = autotest.setupResults) === null || _c === void 0 ? void 0 : _c.map((step) => this.toOriginStep(step)),
74
+ teardownResults: (_d = autotest.teardownResults) === null || _d === void 0 ? void 0 : _d.map((step) => this.toOriginStep(step)),
75
+ attachments: (_e = autotest.attachments) === null || _e === void 0 ? void 0 : _e.map((attachment) => ({ id: attachment.id })),
76
+ message: autotest.message,
77
+ trace: autotest.traces,
78
+ };
79
+ if (autotest.duration !== undefined) {
80
+ model.durationInMs = autotest.duration;
81
+ model.duration = autotest.duration;
82
+ }
83
+ return model;
84
+ }
65
85
  toLocalTestRun(testRun) {
66
86
  var _a, _b, _c, _d;
67
87
  return {
@@ -6,6 +6,9 @@ export declare class TestRunsService extends BaseService implements ITestRunsSer
6
6
  protected readonly config: AdapterConfig;
7
7
  protected _client: TestitApiClient.TestRunsApi;
8
8
  protected _converter: ITestRunConverter;
9
+ private readonly _testResults;
10
+ /** testResultId by autoTestExternalId within current run (InProgress POST + search). */
11
+ private readonly testResultIdsByExternalId;
9
12
  constructor(config: AdapterConfig);
10
13
  createTestRun(): Promise<TestRunId>;
11
14
  getTestRun(testRunId: TestRunId): Promise<TestRunGet>;
@@ -14,5 +17,8 @@ export declare class TestRunsService extends BaseService implements ITestRunsSer
14
17
  completeTestRun(testRunId: TestRunId): Promise<void>;
15
18
  postInProgressAutotestResult(testRunId: string, result: AutotestResult): Promise<void>;
16
19
  loadAutotests(testRunId: string, results: Array<AutotestResult>): Promise<void>;
20
+ private resolveExistingTestResultId;
21
+ private rememberCreatedTestResultId;
17
22
  private sendAutotestResultWithRetry;
23
+ private updateAutotestResultWithRetry;
18
24
  }
@@ -50,6 +50,7 @@ exports.TestRunsService = void 0;
50
50
  const TestitApiClient = __importStar(require("testit-api-client"));
51
51
  const common_1 = require("../../common");
52
52
  const utils_1 = require("../../common/utils");
53
+ const testresults_1 = require("../testresults");
53
54
  const testruns_converter_1 = require("./testruns.converter");
54
55
  const testruns_handler_1 = require("./testruns.handler");
55
56
  const logger_1 = __importDefault(require("../../logger"));
@@ -57,8 +58,11 @@ class TestRunsService extends common_1.BaseService {
57
58
  constructor(config) {
58
59
  super(config);
59
60
  this.config = config;
61
+ /** testResultId by autoTestExternalId within current run (InProgress POST + search). */
62
+ this.testResultIdsByExternalId = new Map();
60
63
  this._client = new TestitApiClient.TestRunsApi();
61
64
  this._converter = new testruns_converter_1.TestRunConverter(config);
65
+ this._testResults = new testresults_1.TestResultsService(config);
62
66
  }
63
67
  createTestRun() {
64
68
  return __awaiter(this, void 0, void 0, function* () {
@@ -70,7 +74,6 @@ class TestRunsService extends common_1.BaseService {
70
74
  .createEmpty({ createEmptyTestRunApiModel: (0, utils_1.escapeHtmlInObject)(createRequest) })
71
75
  // @ts-ignore
72
76
  .then((response) => {
73
- //logger.debug("Full response from createEmpty:", response);
74
77
  const data = response.body || response;
75
78
  if (!data) {
76
79
  throw new Error("API returned undefined response");
@@ -161,16 +164,37 @@ class TestRunsService extends common_1.BaseService {
161
164
  }
162
165
  loadAutotests(testRunId, results) {
163
166
  return __awaiter(this, void 0, void 0, function* () {
164
- var _a, _b;
165
- const autotestResultsForTestRun = results.map((result) => this._converter.toOriginAutotestResult(result));
166
- (0, utils_1.escapeHtmlInObjectArray)(autotestResultsForTestRun);
167
- for (const autotestResult of autotestResultsForTestRun) {
167
+ var _a, _b, _c, _d;
168
+ for (const result of results) {
169
+ const externalId = result.autoTestExternalId;
170
+ const existingId = yield this.resolveExistingTestResultId(externalId);
171
+ if (existingId) {
172
+ (0, utils_1.logTmsLoadTestRun)("PUT updateTestResult (final)", {
173
+ testRunId,
174
+ autoTestExternalId: externalId,
175
+ testResultId: existingId,
176
+ stepCount: (_b = (_a = result.stepResults) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0,
177
+ });
178
+ yield this.updateAutotestResultWithRetry(existingId, result).catch((err) => {
179
+ var _a, _b;
180
+ const normalized = (_b = (_a = err === null || err === void 0 ? void 0 : err.body) !== null && _a !== void 0 ? _a : err === null || err === void 0 ? void 0 : err.error) !== null && _b !== void 0 ? _b : err;
181
+ logger_1.default.error("[testit-js-commons:loadTestRun] FAILED to update final result", {
182
+ testRunId,
183
+ autoTestExternalId: externalId,
184
+ testResultId: existingId,
185
+ error: normalized,
186
+ });
187
+ });
188
+ continue;
189
+ }
190
+ const autotestResult = this._converter.toOriginAutotestResult(result);
191
+ (0, utils_1.escapeHtmlInObject)(autotestResult);
168
192
  (0, utils_1.logTmsLoadTestRun)("POST setAutoTestResults (final)", {
169
193
  testRunId,
170
194
  autoTestExternalId: autotestResult.autoTestExternalId,
171
195
  statusType: autotestResult.statusType,
172
196
  statusCode: autotestResult.statusCode,
173
- stepCount: (_b = (_a = autotestResult.stepResults) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0,
197
+ stepCount: (_d = (_c = autotestResult.stepResults) === null || _c === void 0 ? void 0 : _c.length) !== null && _d !== void 0 ? _d : 0,
174
198
  });
175
199
  yield this.sendAutotestResultWithRetry(testRunId, autotestResult).catch((err) => {
176
200
  var _a, _b;
@@ -184,19 +208,55 @@ class TestRunsService extends common_1.BaseService {
184
208
  }
185
209
  });
186
210
  }
211
+ resolveExistingTestResultId(externalId) {
212
+ return __awaiter(this, void 0, void 0, function* () {
213
+ const cached = this.testResultIdsByExternalId.get(externalId);
214
+ if (cached) {
215
+ return cached;
216
+ }
217
+ const found = yield this._testResults.findTestResultIdByExternalId(externalId);
218
+ if (found) {
219
+ this.testResultIdsByExternalId.set(externalId, found);
220
+ }
221
+ return found;
222
+ });
223
+ }
224
+ rememberCreatedTestResultId(externalId, ids) {
225
+ if (!externalId) {
226
+ return;
227
+ }
228
+ const list = Array.isArray(ids) ? ids : ids != null ? [ids] : [];
229
+ const id = list[0];
230
+ if (typeof id === "string" && id.length > 0) {
231
+ this.testResultIdsByExternalId.set(externalId, id);
232
+ }
233
+ }
187
234
  sendAutotestResultWithRetry(testRunId, autotestResult) {
188
235
  return __awaiter(this, void 0, void 0, function* () {
189
236
  var _a;
190
- yield (0, utils_1.withHttpRetry)(() => this._client.setAutoTestResultsForTestRun(testRunId, {
237
+ const ids = yield (0, utils_1.withHttpRetry)(() => this._client.setAutoTestResultsForTestRun(testRunId, {
191
238
  autoTestResultsForTestRunModel: [autotestResult],
192
239
  }), {
193
240
  label: `setAutoTestResults:${autotestResult.autoTestExternalId}:${(_a = autotestResult.statusCode) !== null && _a !== void 0 ? _a : autotestResult.statusType}`,
194
241
  });
242
+ this.rememberCreatedTestResultId(autotestResult.autoTestExternalId, ids);
195
243
  logger_1.default.debug("[testruns] setAutoTestResults ok", {
196
244
  testRunId,
197
245
  autoTestExternalId: autotestResult.autoTestExternalId,
198
246
  statusCode: autotestResult.statusCode,
199
247
  statusType: autotestResult.statusType,
248
+ testResultId: this.testResultIdsByExternalId.get(autotestResult.autoTestExternalId),
249
+ });
250
+ });
251
+ }
252
+ updateAutotestResultWithRetry(testResultId, result) {
253
+ return __awaiter(this, void 0, void 0, function* () {
254
+ const model = this._converter.toOriginTestResultUpdate(result);
255
+ (0, utils_1.escapeHtmlInObject)(model);
256
+ yield (0, utils_1.withHttpRetry)(() => this._testResults.updateTestResult(testResultId, model), { label: `updateTestResult:${result.autoTestExternalId}` });
257
+ logger_1.default.debug("[testruns] updateTestResult ok", {
258
+ testResultId,
259
+ autoTestExternalId: result.autoTestExternalId,
200
260
  });
201
261
  });
202
262
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testit-js-commons",
3
- "version": "4.1.1",
3
+ "version": "4.1.2-TMS-5.7",
4
4
  "description": "JavaScript commons for Test IT",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -39,7 +39,7 @@
39
39
  },
40
40
  "dependencies": {
41
41
  "dotenv": "^16.0.3",
42
- "testit-api-client": "7.2.6"
42
+ "testit-api-client": "7.2.6-TMS-5.7"
43
43
  },
44
44
  "files": [
45
45
  "lib",