testit-js-commons 4.0.5 → 4.1.0
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.
- package/lib/common/types/config.type.d.ts +4 -0
- package/lib/common/utils/http-retry.util.d.ts +29 -0
- package/lib/common/utils/http-retry.util.js +125 -0
- package/lib/common/utils/http-retry.util.test.d.ts +1 -0
- package/lib/common/utils/http-retry.util.test.js +23 -0
- package/lib/common/utils/index.d.ts +1 -0
- package/lib/common/utils/index.js +1 -0
- package/lib/common/utils/tms-load-test-run-debug.d.ts +0 -1
- package/lib/common/utils/tms-load-test-run-debug.js +10 -3
- package/lib/helpers/config/config.helper.js +26 -11
- package/lib/helpers/config/config.helper.test.js +24 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +6 -0
- package/lib/logger/index.d.ts +17 -0
- package/lib/logger/index.js +65 -0
- package/lib/services/attachments/attachments.service.js +18 -65
- package/lib/services/autotests/autotests.handler.js +5 -1
- package/lib/services/autotests/autotests.service.d.ts +0 -2
- package/lib/services/autotests/autotests.service.js +32 -35
- package/lib/services/syncstorage/syncstorage.runner.js +16 -12
- package/lib/services/testresults/testresults.handler.js +5 -1
- package/lib/services/testruns/testruns.handler.js +6 -2
- package/lib/services/testruns/testruns.service.js +21 -29
- package/lib/strategy/base.strategy.js +13 -1
- package/package.json +1 -1
|
@@ -12,6 +12,7 @@ export interface CliOptions {
|
|
|
12
12
|
tmsAutomaticUpdationLinksToTestCases: boolean;
|
|
13
13
|
tmsSyncStorageEnabled: boolean;
|
|
14
14
|
tmsSyncStoragePort: string;
|
|
15
|
+
tmsImportRealtime: boolean;
|
|
15
16
|
}
|
|
16
17
|
export interface EnvironmentOptions {
|
|
17
18
|
TMS_URL: string;
|
|
@@ -27,6 +28,7 @@ export interface EnvironmentOptions {
|
|
|
27
28
|
TMS_CERT_VALIDATION: string;
|
|
28
29
|
TMS_SYNC_STORAGE_ENABLED: string;
|
|
29
30
|
TMS_SYNC_STORAGE_PORT: string;
|
|
31
|
+
TMS_IMPORT_REALTIME: string;
|
|
30
32
|
}
|
|
31
33
|
export interface ProcessEnvOptions {
|
|
32
34
|
TMS_URL?: string;
|
|
@@ -42,6 +44,7 @@ export interface ProcessEnvOptions {
|
|
|
42
44
|
TMS_CERT_VALIDATION?: string;
|
|
43
45
|
TMS_SYNC_STORAGE_ENABLED?: string;
|
|
44
46
|
TMS_SYNC_STORAGE_PORT?: string;
|
|
47
|
+
TMS_IMPORT_REALTIME?: string;
|
|
45
48
|
}
|
|
46
49
|
export interface AdapterConfig {
|
|
47
50
|
url: string;
|
|
@@ -56,4 +59,5 @@ export interface AdapterConfig {
|
|
|
56
59
|
certValidation?: boolean;
|
|
57
60
|
syncStorageEnabled?: boolean;
|
|
58
61
|
syncStoragePort?: string;
|
|
62
|
+
importRealtime?: boolean;
|
|
59
63
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type HttpErrorLike = {
|
|
2
|
+
code?: string;
|
|
3
|
+
errno?: number;
|
|
4
|
+
status?: number;
|
|
5
|
+
statusCode?: number;
|
|
6
|
+
response?: {
|
|
7
|
+
status?: number;
|
|
8
|
+
};
|
|
9
|
+
error?: unknown;
|
|
10
|
+
cause?: unknown;
|
|
11
|
+
};
|
|
12
|
+
export type HttpRetryOptions = {
|
|
13
|
+
maxAttempts?: number;
|
|
14
|
+
delayMs?: number;
|
|
15
|
+
/** When true, delay is `delayMs * attempt` (1-based). */
|
|
16
|
+
backoff?: boolean;
|
|
17
|
+
/** Shown in debug logs when a retry happens. */
|
|
18
|
+
label?: string;
|
|
19
|
+
};
|
|
20
|
+
/** Unwrap nested `error` / `cause` from API client wrappers. */
|
|
21
|
+
export declare function unwrapHttpError(err: unknown): HttpErrorLike | null;
|
|
22
|
+
export declare function getHttpStatus(err: HttpErrorLike): number | undefined;
|
|
23
|
+
export declare function getNetworkErrorCode(err: HttpErrorLike): string | undefined;
|
|
24
|
+
/**
|
|
25
|
+
* Retry only transient failures: HTTP 5xx and known network error codes.
|
|
26
|
+
* Does not retry HTTP 4xx (client/validation errors).
|
|
27
|
+
*/
|
|
28
|
+
export declare function isRetryableHttpError(err: unknown): boolean;
|
|
29
|
+
export declare function withHttpRetry<T>(fn: () => Promise<T>, options?: HttpRetryOptions): Promise<T>;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.unwrapHttpError = unwrapHttpError;
|
|
16
|
+
exports.getHttpStatus = getHttpStatus;
|
|
17
|
+
exports.getNetworkErrorCode = getNetworkErrorCode;
|
|
18
|
+
exports.isRetryableHttpError = isRetryableHttpError;
|
|
19
|
+
exports.withHttpRetry = withHttpRetry;
|
|
20
|
+
const logger_1 = __importDefault(require("../../logger"));
|
|
21
|
+
/** Node/network error codes that are safe to retry. */
|
|
22
|
+
const TRANSIENT_NETWORK_CODES = new Set(["ECONNRESET",
|
|
23
|
+
"ETIMEDOUT",
|
|
24
|
+
"EPIPE",
|
|
25
|
+
"ECONNABORTED",
|
|
26
|
+
"ECONNREFUSED",
|
|
27
|
+
"EHOSTUNREACH",
|
|
28
|
+
"ENETUNREACH",
|
|
29
|
+
"EAI_AGAIN",
|
|
30
|
+
]);
|
|
31
|
+
/** Windows errno for ECONNRESET. */
|
|
32
|
+
const ERRNO_ECONNRESET = -104;
|
|
33
|
+
function sleep(ms) {
|
|
34
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
35
|
+
}
|
|
36
|
+
function isErrorLike(value) {
|
|
37
|
+
return value !== null && typeof value === "object";
|
|
38
|
+
}
|
|
39
|
+
/** Unwrap nested `error` / `cause` from API client wrappers. */
|
|
40
|
+
function unwrapHttpError(err) {
|
|
41
|
+
if (!isErrorLike(err)) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
const nested = err.error;
|
|
45
|
+
if (isErrorLike(nested)) {
|
|
46
|
+
return nested;
|
|
47
|
+
}
|
|
48
|
+
const cause = err.cause;
|
|
49
|
+
if (isErrorLike(cause)) {
|
|
50
|
+
return cause;
|
|
51
|
+
}
|
|
52
|
+
return err;
|
|
53
|
+
}
|
|
54
|
+
function getHttpStatus(err) {
|
|
55
|
+
var _a, _b, _c;
|
|
56
|
+
const status = (_b = (_a = err.status) !== null && _a !== void 0 ? _a : err.statusCode) !== null && _b !== void 0 ? _b : (_c = err.response) === null || _c === void 0 ? void 0 : _c.status;
|
|
57
|
+
return typeof status === "number" ? status : undefined;
|
|
58
|
+
}
|
|
59
|
+
function getNetworkErrorCode(err) {
|
|
60
|
+
return typeof err.code === "string" ? err.code : undefined;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Retry only transient failures: HTTP 5xx and known network error codes.
|
|
64
|
+
* Does not retry HTTP 4xx (client/validation errors).
|
|
65
|
+
*/
|
|
66
|
+
function isRetryableHttpError(err) {
|
|
67
|
+
const e = unwrapHttpError(err);
|
|
68
|
+
if (!e) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
const status = getHttpStatus(e);
|
|
72
|
+
if (status !== undefined) {
|
|
73
|
+
if (status >= 400 && status < 500) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
if (status >= 500) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const code = getNetworkErrorCode(e);
|
|
81
|
+
if (code !== undefined && TRANSIENT_NETWORK_CODES.has(code)) {
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
if (e.errno === ERRNO_ECONNRESET) {
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
function withHttpRetry(fn_1) {
|
|
90
|
+
return __awaiter(this, arguments, void 0, function* (fn, options = {}) {
|
|
91
|
+
var _a, _b, _c, _d, _e, _f;
|
|
92
|
+
const maxAttempts = (_a = options.maxAttempts) !== null && _a !== void 0 ? _a : 3;
|
|
93
|
+
const delayMs = (_b = options.delayMs) !== null && _b !== void 0 ? _b : 1000;
|
|
94
|
+
const backoff = (_c = options.backoff) !== null && _c !== void 0 ? _c : false;
|
|
95
|
+
const label = (_d = options.label) !== null && _d !== void 0 ? _d : "http";
|
|
96
|
+
let last;
|
|
97
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
98
|
+
try {
|
|
99
|
+
const value = yield fn();
|
|
100
|
+
if (attempt > 1) {
|
|
101
|
+
logger_1.default.debug(`[http-retry] ${label} succeeded on attempt ${attempt}/${maxAttempts}`);
|
|
102
|
+
}
|
|
103
|
+
return value;
|
|
104
|
+
}
|
|
105
|
+
catch (e) {
|
|
106
|
+
last = e;
|
|
107
|
+
if (!isRetryableHttpError(e) || attempt === maxAttempts) {
|
|
108
|
+
if (attempt > 1) {
|
|
109
|
+
logger_1.default.debug(`[http-retry] ${label} failed after ${attempt} attempt(s)`, {
|
|
110
|
+
retryable: isRetryableHttpError(e),
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
throw e;
|
|
114
|
+
}
|
|
115
|
+
const waitMs = backoff ? delayMs * attempt : delayMs;
|
|
116
|
+
logger_1.default.debug(`[http-retry] ${label} attempt ${attempt}/${maxAttempts} failed, retry in ${waitMs}ms`, {
|
|
117
|
+
status: getHttpStatus((_e = unwrapHttpError(e)) !== null && _e !== void 0 ? _e : {}),
|
|
118
|
+
code: getNetworkErrorCode((_f = unwrapHttpError(e)) !== null && _f !== void 0 ? _f : {}),
|
|
119
|
+
});
|
|
120
|
+
yield sleep(waitMs);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
throw last;
|
|
124
|
+
});
|
|
125
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const http_retry_util_1 = require("./http-retry.util");
|
|
4
|
+
describe("isRetryableHttpError", () => {
|
|
5
|
+
it("retries on network error code", () => {
|
|
6
|
+
expect((0, http_retry_util_1.isRetryableHttpError)({ code: "ECONNRESET" })).toBe(true);
|
|
7
|
+
expect((0, http_retry_util_1.getNetworkErrorCode)({ code: "ECONNRESET" })).toBe("ECONNRESET");
|
|
8
|
+
});
|
|
9
|
+
it("retries on HTTP 5xx", () => {
|
|
10
|
+
expect((0, http_retry_util_1.isRetryableHttpError)({ status: 503 })).toBe(true);
|
|
11
|
+
expect((0, http_retry_util_1.getHttpStatus)({ response: { status: 502 } })).toBe(502);
|
|
12
|
+
});
|
|
13
|
+
it("does not retry HTTP 4xx", () => {
|
|
14
|
+
expect((0, http_retry_util_1.isRetryableHttpError)({ status: 400 })).toBe(false);
|
|
15
|
+
expect((0, http_retry_util_1.isRetryableHttpError)({ statusCode: 404 })).toBe(false);
|
|
16
|
+
});
|
|
17
|
+
it("unwraps nested error", () => {
|
|
18
|
+
expect((0, http_retry_util_1.isRetryableHttpError)({ error: { code: "ETIMEDOUT" } })).toBe(true);
|
|
19
|
+
});
|
|
20
|
+
it("does not retry unknown errors without status/code", () => {
|
|
21
|
+
expect((0, http_retry_util_1.isRetryableHttpError)(new Error("something went wrong"))).toBe(false);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -17,3 +17,4 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
17
17
|
__exportStar(require("./utils"), exports);
|
|
18
18
|
__exportStar(require("./html-escape.util"), exports);
|
|
19
19
|
__exportStar(require("./tms-load-test-run-debug"), exports);
|
|
20
|
+
__exportStar(require("./http-retry.util"), exports);
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
3
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
6
|
exports.isTmsLoadTestRunDebug = isTmsLoadTestRunDebug;
|
|
5
7
|
exports.logTmsLoadTestRun = logTmsLoadTestRun;
|
|
8
|
+
/** Trace loadTestRun / result POST with `LOG_LEVEL=debug` or `TMS_DEBUG_LOAD_TEST_RUN=1`. */
|
|
9
|
+
const logger_1 = __importDefault(require("../../logger"));
|
|
6
10
|
function isTmsLoadTestRunDebug() {
|
|
11
|
+
if (logger_1.default.isLevelEnabled("debug")) {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
7
14
|
const v = process.env.TMS_DEBUG_LOAD_TEST_RUN;
|
|
8
15
|
return v === "1" || (v === null || v === void 0 ? void 0 : v.toLowerCase()) === "true";
|
|
9
16
|
}
|
|
@@ -12,9 +19,9 @@ function logTmsLoadTestRun(message, data) {
|
|
|
12
19
|
return;
|
|
13
20
|
}
|
|
14
21
|
if (data && Object.keys(data).length > 0) {
|
|
15
|
-
|
|
22
|
+
logger_1.default.debug(`[loadTestRun] ${message}`, data);
|
|
16
23
|
}
|
|
17
24
|
else {
|
|
18
|
-
|
|
25
|
+
logger_1.default.debug(`[loadTestRun] ${message}`);
|
|
19
26
|
}
|
|
20
27
|
}
|
|
@@ -32,10 +32,14 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
32
32
|
return result;
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
35
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
39
|
exports.ConfigComposer = exports.DEFAULT_CONFIG_FILE = void 0;
|
|
37
40
|
const dotenv = __importStar(require("dotenv"));
|
|
38
41
|
const common_1 = require("../../common");
|
|
42
|
+
const logger_1 = __importDefault(require("../../logger"));
|
|
39
43
|
exports.DEFAULT_CONFIG_FILE = "tms.config.json";
|
|
40
44
|
class ConfigComposer {
|
|
41
45
|
compose(base) {
|
|
@@ -48,7 +52,7 @@ class ConfigComposer {
|
|
|
48
52
|
if (content !== "") {
|
|
49
53
|
const fileConfig = JSON.parse(content);
|
|
50
54
|
if (fileConfig.privateToken) {
|
|
51
|
-
|
|
55
|
+
logger_1.default.warn(`
|
|
52
56
|
The configuration file specifies a private token. It is not safe.
|
|
53
57
|
Use TMS_PRIVATE_TOKEN environment variable`);
|
|
54
58
|
}
|
|
@@ -58,10 +62,17 @@ class ConfigComposer {
|
|
|
58
62
|
config = this.merge(environment, base);
|
|
59
63
|
}
|
|
60
64
|
this.validateConfig(config);
|
|
65
|
+
logger_1.default.debug("[config] composed", {
|
|
66
|
+
adapterMode: config.adapterMode,
|
|
67
|
+
importRealtime: config.importRealtime,
|
|
68
|
+
syncStorageEnabled: config.syncStorageEnabled,
|
|
69
|
+
hasTestRunId: Boolean(config.testRunId),
|
|
70
|
+
projectId: config.projectId,
|
|
71
|
+
});
|
|
61
72
|
return config;
|
|
62
73
|
}
|
|
63
74
|
mergeAll(file, env, base) {
|
|
64
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
|
|
75
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
|
|
65
76
|
return {
|
|
66
77
|
url: this.resolveAllProperties(file.url, env === null || env === void 0 ? void 0 : env.TMS_URL, base === null || base === void 0 ? void 0 : base.url),
|
|
67
78
|
projectId: this.resolveAllProperties(file.projectId, env === null || env === void 0 ? void 0 : env.TMS_PROJECT_ID, base === null || base === void 0 ? void 0 : base.projectId),
|
|
@@ -75,10 +86,11 @@ class ConfigComposer {
|
|
|
75
86
|
certValidation: (_m = (_l = (_k = file.certValidation) !== null && _k !== void 0 ? _k : stringToBoolean(env === null || env === void 0 ? void 0 : env.TMS_CERT_VALIDATION)) !== null && _l !== void 0 ? _l : base === null || base === void 0 ? void 0 : base.certValidation) !== null && _m !== void 0 ? _m : true,
|
|
76
87
|
syncStorageEnabled: (_q = (_p = (_o = file.syncStorageEnabled) !== null && _o !== void 0 ? _o : stringToBoolean(env === null || env === void 0 ? void 0 : env.TMS_SYNC_STORAGE_ENABLED)) !== null && _p !== void 0 ? _p : base === null || base === void 0 ? void 0 : base.syncStorageEnabled) !== null && _q !== void 0 ? _q : true,
|
|
77
88
|
syncStoragePort: this.resolveAllProperties(file.syncStoragePort, env === null || env === void 0 ? void 0 : env.TMS_SYNC_STORAGE_PORT, base === null || base === void 0 ? void 0 : base.syncStoragePort) || "49152",
|
|
89
|
+
importRealtime: (_t = (_s = (_r = file.importRealtime) !== null && _r !== void 0 ? _r : stringToBoolean(env === null || env === void 0 ? void 0 : env.TMS_IMPORT_REALTIME)) !== null && _s !== void 0 ? _s : base === null || base === void 0 ? void 0 : base.importRealtime) !== null && _t !== void 0 ? _t : false,
|
|
78
90
|
};
|
|
79
91
|
}
|
|
80
92
|
merge(env, base) {
|
|
81
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
93
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
82
94
|
return {
|
|
83
95
|
url: this.resolveProperties(env === null || env === void 0 ? void 0 : env.TMS_URL, base === null || base === void 0 ? void 0 : base.url),
|
|
84
96
|
projectId: this.resolveProperties(env === null || env === void 0 ? void 0 : env.TMS_PROJECT_ID, base === null || base === void 0 ? void 0 : base.projectId),
|
|
@@ -92,10 +104,11 @@ class ConfigComposer {
|
|
|
92
104
|
certValidation: (_h = (_g = stringToBoolean(env === null || env === void 0 ? void 0 : env.TMS_CERT_VALIDATION)) !== null && _g !== void 0 ? _g : base === null || base === void 0 ? void 0 : base.certValidation) !== null && _h !== void 0 ? _h : true,
|
|
93
105
|
syncStorageEnabled: (_k = (_j = stringToBoolean(env === null || env === void 0 ? void 0 : env.TMS_SYNC_STORAGE_ENABLED)) !== null && _j !== void 0 ? _j : base === null || base === void 0 ? void 0 : base.syncStorageEnabled) !== null && _k !== void 0 ? _k : true,
|
|
94
106
|
syncStoragePort: this.resolveProperties(env === null || env === void 0 ? void 0 : env.TMS_SYNC_STORAGE_PORT, base === null || base === void 0 ? void 0 : base.syncStoragePort) || "49152",
|
|
107
|
+
importRealtime: (_m = (_l = stringToBoolean(env === null || env === void 0 ? void 0 : env.TMS_IMPORT_REALTIME)) !== null && _l !== void 0 ? _l : base === null || base === void 0 ? void 0 : base.importRealtime) !== null && _m !== void 0 ? _m : false,
|
|
95
108
|
};
|
|
96
109
|
}
|
|
97
110
|
mergeEnv(dotEnv, processEnv) {
|
|
98
|
-
var _a, _b, _c, _d, _e;
|
|
111
|
+
var _a, _b, _c, _d, _e, _f;
|
|
99
112
|
return {
|
|
100
113
|
TMS_URL: this.resolveProperties(dotEnv === null || dotEnv === void 0 ? void 0 : dotEnv.TMS_URL, processEnv === null || processEnv === void 0 ? void 0 : processEnv.TMS_URL),
|
|
101
114
|
TMS_PROJECT_ID: this.resolveProperties(dotEnv === null || dotEnv === void 0 ? void 0 : dotEnv.TMS_PROJECT_ID, processEnv === null || processEnv === void 0 ? void 0 : processEnv.TMS_PROJECT_ID),
|
|
@@ -109,6 +122,7 @@ class ConfigComposer {
|
|
|
109
122
|
TMS_CERT_VALIDATION: (_d = dotEnv === null || dotEnv === void 0 ? void 0 : dotEnv.TMS_CERT_VALIDATION) !== null && _d !== void 0 ? _d : processEnv === null || processEnv === void 0 ? void 0 : processEnv.TMS_CERT_VALIDATION,
|
|
110
123
|
TMS_SYNC_STORAGE_ENABLED: (_e = dotEnv === null || dotEnv === void 0 ? void 0 : dotEnv.TMS_SYNC_STORAGE_ENABLED) !== null && _e !== void 0 ? _e : processEnv === null || processEnv === void 0 ? void 0 : processEnv.TMS_SYNC_STORAGE_ENABLED,
|
|
111
124
|
TMS_SYNC_STORAGE_PORT: this.resolveProperties(dotEnv === null || dotEnv === void 0 ? void 0 : dotEnv.TMS_SYNC_STORAGE_PORT, processEnv === null || processEnv === void 0 ? void 0 : processEnv.TMS_SYNC_STORAGE_PORT),
|
|
125
|
+
TMS_IMPORT_REALTIME: (_f = dotEnv === null || dotEnv === void 0 ? void 0 : dotEnv.TMS_IMPORT_REALTIME) !== null && _f !== void 0 ? _f : processEnv === null || processEnv === void 0 ? void 0 : processEnv.TMS_IMPORT_REALTIME,
|
|
112
126
|
};
|
|
113
127
|
}
|
|
114
128
|
resolveAllProperties(file, env, base) {
|
|
@@ -141,30 +155,30 @@ class ConfigComposer {
|
|
|
141
155
|
new URL(config.url);
|
|
142
156
|
}
|
|
143
157
|
catch (err) {
|
|
144
|
-
|
|
158
|
+
logger_1.default.error(`Url is invalid`);
|
|
145
159
|
}
|
|
146
160
|
if (!config.privateToken) {
|
|
147
|
-
|
|
161
|
+
logger_1.default.error(`Private Token is invalid`);
|
|
148
162
|
}
|
|
149
163
|
if (config.projectId.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) {
|
|
150
|
-
|
|
164
|
+
logger_1.default.error(`Project ID is invalid`);
|
|
151
165
|
}
|
|
152
166
|
if (config.configurationId.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) {
|
|
153
|
-
|
|
167
|
+
logger_1.default.error(`Configuration ID is invalid`);
|
|
154
168
|
}
|
|
155
169
|
if (config.adapterMode == 2) {
|
|
156
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) {
|
|
157
|
-
|
|
171
|
+
logger_1.default.error(`Adapter works in mode 2. Config should not contains test run id.`);
|
|
158
172
|
}
|
|
159
173
|
}
|
|
160
174
|
else if (config.adapterMode == 1) {
|
|
161
175
|
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) {
|
|
162
|
-
|
|
176
|
+
logger_1.default.error(`Adapter works in mode 1. Config should contains valid test run id.`);
|
|
163
177
|
}
|
|
164
178
|
}
|
|
165
179
|
else {
|
|
166
180
|
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) {
|
|
167
|
-
|
|
181
|
+
logger_1.default.error(`Adapter works in mode 0. Config should contains valid test run id.`);
|
|
168
182
|
}
|
|
169
183
|
}
|
|
170
184
|
}
|
|
@@ -188,6 +202,7 @@ function parseProcessEnvConfig() {
|
|
|
188
202
|
TMS_CONFIG_FILE: process.env.TMS_PRIVATE_TOKEN,
|
|
189
203
|
TMS_SYNC_STORAGE_ENABLED: process.env.TMS_SYNC_STORAGE_ENABLED,
|
|
190
204
|
TMS_SYNC_STORAGE_PORT: process.env.TMS_SYNC_STORAGE_PORT,
|
|
205
|
+
TMS_IMPORT_REALTIME: process.env.TMS_IMPORT_REALTIME,
|
|
191
206
|
};
|
|
192
207
|
}
|
|
193
208
|
function stringToAdapterMode(str) {
|
|
@@ -8,6 +8,7 @@ describe("ConfigComposer sync-storage defaults", () => {
|
|
|
8
8
|
process.env = Object.assign({}, env);
|
|
9
9
|
delete process.env.TMS_SYNC_STORAGE_ENABLED;
|
|
10
10
|
delete process.env.TMS_SYNC_STORAGE_PORT;
|
|
11
|
+
delete process.env.TMS_IMPORT_REALTIME;
|
|
11
12
|
});
|
|
12
13
|
afterAll(() => {
|
|
13
14
|
process.env = env;
|
|
@@ -22,6 +23,7 @@ describe("ConfigComposer sync-storage defaults", () => {
|
|
|
22
23
|
});
|
|
23
24
|
expect(config.syncStorageEnabled).toBe(true);
|
|
24
25
|
expect(config.syncStoragePort).toBe("49152");
|
|
26
|
+
expect(config.importRealtime).toBe(false);
|
|
25
27
|
});
|
|
26
28
|
it("should allow disabling sync-storage via env", () => {
|
|
27
29
|
const config = new config_helper_1.ConfigComposer().merge({
|
|
@@ -37,4 +39,26 @@ describe("ConfigComposer sync-storage defaults", () => {
|
|
|
37
39
|
expect(config.syncStorageEnabled).toBe(false);
|
|
38
40
|
expect(config.syncStoragePort).toBe("59999");
|
|
39
41
|
});
|
|
42
|
+
it("should keep importRealtime false by default", () => {
|
|
43
|
+
const config = new config_helper_1.ConfigComposer().merge(undefined, {
|
|
44
|
+
url: "http://localhost:8080",
|
|
45
|
+
privateToken: "token",
|
|
46
|
+
projectId: "11111111-1111-1111-1111-111111111111",
|
|
47
|
+
configurationId: "22222222-2222-2222-2222-222222222222",
|
|
48
|
+
testRunId: "33333333-3333-3333-3333-333333333333",
|
|
49
|
+
});
|
|
50
|
+
expect(config.importRealtime).toBe(false);
|
|
51
|
+
});
|
|
52
|
+
it("should allow enabling importRealtime via env", () => {
|
|
53
|
+
const config = new config_helper_1.ConfigComposer().merge({
|
|
54
|
+
TMS_IMPORT_REALTIME: "true",
|
|
55
|
+
}, {
|
|
56
|
+
url: "http://localhost:8080",
|
|
57
|
+
privateToken: "token",
|
|
58
|
+
projectId: "11111111-1111-1111-1111-111111111111",
|
|
59
|
+
configurationId: "22222222-2222-2222-2222-222222222222",
|
|
60
|
+
testRunId: "33333333-3333-3333-3333-333333333333",
|
|
61
|
+
});
|
|
62
|
+
expect(config.importRealtime).toBe(true);
|
|
63
|
+
});
|
|
40
64
|
});
|
package/lib/index.d.ts
CHANGED
package/lib/index.js
CHANGED
|
@@ -13,10 +13,16 @@ var __createBinding = (this && this.__createBinding) || (Object.create ? (functi
|
|
|
13
13
|
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
17
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
18
|
+
};
|
|
16
19
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.logger = void 0;
|
|
17
21
|
__exportStar(require("./common"), exports);
|
|
18
22
|
__exportStar(require("./client"), exports);
|
|
19
23
|
__exportStar(require("./helpers"), exports);
|
|
20
24
|
__exportStar(require("./services"), exports);
|
|
21
25
|
__exportStar(require("./storage"), exports);
|
|
22
26
|
__exportStar(require("./strategy"), exports);
|
|
27
|
+
var logger_1 = require("./logger");
|
|
28
|
+
Object.defineProperty(exports, "logger", { enumerable: true, get: function () { return __importDefault(logger_1).default; } });
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
type LogLevel = 'error' | 'warn' | 'info' | 'debug';
|
|
2
|
+
declare class Logger {
|
|
3
|
+
private readonly levels;
|
|
4
|
+
private currentLevel;
|
|
5
|
+
constructor();
|
|
6
|
+
private log_;
|
|
7
|
+
error(message: string, ...args: unknown[]): void;
|
|
8
|
+
warn(message: string, ...args: unknown[]): void;
|
|
9
|
+
info(message: string, ...args: unknown[]): void;
|
|
10
|
+
log(message: string, ...args: unknown[]): void;
|
|
11
|
+
debug(message: string, ...args: unknown[]): void;
|
|
12
|
+
isLevelEnabled(level: LogLevel): boolean;
|
|
13
|
+
getCurrentLevel(): LogLevel;
|
|
14
|
+
setLevel(level: LogLevel): void;
|
|
15
|
+
}
|
|
16
|
+
declare const logger: Logger;
|
|
17
|
+
export default logger;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
class Logger {
|
|
4
|
+
constructor() {
|
|
5
|
+
this.levels = {
|
|
6
|
+
error: 0,
|
|
7
|
+
warn: 1,
|
|
8
|
+
info: 2,
|
|
9
|
+
debug: 3
|
|
10
|
+
};
|
|
11
|
+
// Уровень из переменной окружения или по умолчанию 'warn'
|
|
12
|
+
const envLevel = (process.env.LOG_LEVEL || 'warn').toLowerCase();
|
|
13
|
+
this.currentLevel = this.levels[envLevel] !== undefined
|
|
14
|
+
? this.levels[envLevel]
|
|
15
|
+
: this.levels.info;
|
|
16
|
+
}
|
|
17
|
+
log_(level, message, ...args) {
|
|
18
|
+
if (this.levels[level] <= this.currentLevel) {
|
|
19
|
+
const timestamp = new Date().toISOString();
|
|
20
|
+
const prefix = `[${timestamp}] [${level.toUpperCase()}]`;
|
|
21
|
+
if (level === 'error') {
|
|
22
|
+
console.error(prefix, message, ...args);
|
|
23
|
+
}
|
|
24
|
+
else if (level === 'warn') {
|
|
25
|
+
console.warn(prefix, message, ...args);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
console.log(prefix, message, ...args);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
error(message, ...args) {
|
|
33
|
+
this.log_('error', message, ...args);
|
|
34
|
+
}
|
|
35
|
+
warn(message, ...args) {
|
|
36
|
+
this.log_('warn', message, ...args);
|
|
37
|
+
}
|
|
38
|
+
info(message, ...args) {
|
|
39
|
+
this.log_('info', message, ...args);
|
|
40
|
+
}
|
|
41
|
+
log(message, ...args) {
|
|
42
|
+
this.log_('info', message, ...args);
|
|
43
|
+
}
|
|
44
|
+
debug(message, ...args) {
|
|
45
|
+
this.log_('debug', message, ...args);
|
|
46
|
+
}
|
|
47
|
+
// Проверка, включен ли уровень
|
|
48
|
+
isLevelEnabled(level) {
|
|
49
|
+
return this.levels[level] <= this.currentLevel;
|
|
50
|
+
}
|
|
51
|
+
// Получить текущий уровень логирования
|
|
52
|
+
getCurrentLevel() {
|
|
53
|
+
const level = Object.keys(this.levels).find((key) => this.levels[key] === this.currentLevel);
|
|
54
|
+
return level || 'info';
|
|
55
|
+
}
|
|
56
|
+
// Установить уровень логирования динамически
|
|
57
|
+
setLevel(level) {
|
|
58
|
+
if (this.levels[level] !== undefined) {
|
|
59
|
+
this.currentLevel = this.levels[level];
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// Синглтон
|
|
64
|
+
const logger = new Logger();
|
|
65
|
+
exports.default = logger;
|
|
@@ -41,6 +41,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
41
41
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
42
42
|
});
|
|
43
43
|
};
|
|
44
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
45
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
46
|
+
};
|
|
44
47
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
48
|
exports.AttachmentsService = void 0;
|
|
46
49
|
// @ts-ignore
|
|
@@ -48,64 +51,10 @@ const TestitApiClient = __importStar(require("testit-api-client"));
|
|
|
48
51
|
const common_1 = require("../../common");
|
|
49
52
|
const buffer_1 = require("buffer");
|
|
50
53
|
const fs = __importStar(require("fs"));
|
|
51
|
-
const
|
|
52
|
-
const
|
|
54
|
+
const logger_1 = __importDefault(require("../../logger"));
|
|
55
|
+
const UPLOAD_RETRY_OPTIONS = { maxAttempts: 5, delayMs: 500, backoff: true };
|
|
53
56
|
/** Minimum HTTP timeout for attachment POST (ms); reduces false timeouts on slow TLS. */
|
|
54
57
|
const UPLOAD_CLIENT_TIMEOUT_MS = 120000;
|
|
55
|
-
function sleep(ms) {
|
|
56
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
57
|
-
}
|
|
58
|
-
function unwrapAttachmentError(err) {
|
|
59
|
-
if (!err || typeof err !== "object") {
|
|
60
|
-
return err;
|
|
61
|
-
}
|
|
62
|
-
const nested = err.error;
|
|
63
|
-
if (nested instanceof Error || (nested && typeof nested === "object")) {
|
|
64
|
-
return nested;
|
|
65
|
-
}
|
|
66
|
-
const cause = err.cause;
|
|
67
|
-
if (cause instanceof Error || (cause && typeof cause === "object")) {
|
|
68
|
-
return cause;
|
|
69
|
-
}
|
|
70
|
-
return err;
|
|
71
|
-
}
|
|
72
|
-
function isTransientAttachmentError(err) {
|
|
73
|
-
var _a, _b, _c, _d, _e;
|
|
74
|
-
const e = unwrapAttachmentError(err);
|
|
75
|
-
const status = (_b = (_a = e === null || e === void 0 ? void 0 : e.response) === null || _a === void 0 ? void 0 : _a.status) !== null && _b !== void 0 ? _b : (_c = err === null || err === void 0 ? void 0 : err.response) === null || _c === void 0 ? void 0 : _c.status;
|
|
76
|
-
if (typeof status === "number" && status >= 400 && status < 500)
|
|
77
|
-
return false;
|
|
78
|
-
if (typeof status === "number" && status >= 500)
|
|
79
|
-
return true;
|
|
80
|
-
const code = (_d = e === null || e === void 0 ? void 0 : e.code) !== null && _d !== void 0 ? _d : err === null || err === void 0 ? void 0 : err.code;
|
|
81
|
-
if (code === "ECONNRESET" || code === "ETIMEDOUT" || code === "EPIPE" || code === "ECONNABORTED")
|
|
82
|
-
return true;
|
|
83
|
-
if ((e === null || e === void 0 ? void 0 : e.errno) === -104 || (err === null || err === void 0 ? void 0 : err.errno) === -104)
|
|
84
|
-
return true;
|
|
85
|
-
const msg = (_e = e === null || e === void 0 ? void 0 : e.message) !== null && _e !== void 0 ? _e : err === null || err === void 0 ? void 0 : err.message;
|
|
86
|
-
if (typeof msg === "string" && /socket hang up|ECONNRESET|ETIMEDOUT|ECONNREFUSED|read ECONNRESET/i.test(msg)) {
|
|
87
|
-
return true;
|
|
88
|
-
}
|
|
89
|
-
return false;
|
|
90
|
-
}
|
|
91
|
-
function withUploadRetry(fn) {
|
|
92
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
93
|
-
let last;
|
|
94
|
-
for (let attempt = 1; attempt <= UPLOAD_MAX_ATTEMPTS; attempt++) {
|
|
95
|
-
try {
|
|
96
|
-
return yield fn();
|
|
97
|
-
}
|
|
98
|
-
catch (e) {
|
|
99
|
-
last = e;
|
|
100
|
-
if (!isTransientAttachmentError(e) || attempt === UPLOAD_MAX_ATTEMPTS) {
|
|
101
|
-
throw e;
|
|
102
|
-
}
|
|
103
|
-
yield sleep(UPLOAD_RETRY_BASE_MS * attempt);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
throw last;
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
58
|
class AttachmentsService extends common_1.BaseService {
|
|
110
59
|
constructor(config) {
|
|
111
60
|
super(config);
|
|
@@ -126,7 +75,8 @@ class AttachmentsService extends common_1.BaseService {
|
|
|
126
75
|
const tempFilePath = `${tempDir}/${fileName}`;
|
|
127
76
|
try {
|
|
128
77
|
fs.writeFileSync(tempFilePath, bufferContent);
|
|
129
|
-
|
|
78
|
+
logger_1.default.debug("[attachments] upload text", { fileName, bytes: bufferContent.length });
|
|
79
|
+
const id = yield (0, common_1.withHttpRetry)(() => __awaiter(this, void 0, void 0, function* () {
|
|
130
80
|
const fileStream = fs.createReadStream(tempFilePath);
|
|
131
81
|
try {
|
|
132
82
|
// @ts-ignore
|
|
@@ -137,7 +87,8 @@ class AttachmentsService extends common_1.BaseService {
|
|
|
137
87
|
finally {
|
|
138
88
|
fileStream.destroy();
|
|
139
89
|
}
|
|
140
|
-
}));
|
|
90
|
+
}), Object.assign(Object.assign({}, UPLOAD_RETRY_OPTIONS), { label: `uploadText:${fileName}` }));
|
|
91
|
+
logger_1.default.debug("[attachments] upload text ok", { fileName, id });
|
|
141
92
|
return [{ id }];
|
|
142
93
|
}
|
|
143
94
|
finally {
|
|
@@ -150,14 +101,14 @@ class AttachmentsService extends common_1.BaseService {
|
|
|
150
101
|
}
|
|
151
102
|
}
|
|
152
103
|
catch (cleanupError) {
|
|
153
|
-
|
|
104
|
+
logger_1.default.warn("Failed to cleanup temporary files:", cleanupError);
|
|
154
105
|
}
|
|
155
106
|
}
|
|
156
107
|
}
|
|
157
108
|
catch (error) {
|
|
158
|
-
|
|
109
|
+
logger_1.default.error("Error uploading text attachment:", error);
|
|
159
110
|
if (error.response) {
|
|
160
|
-
|
|
111
|
+
logger_1.default.error("Response details:", {
|
|
161
112
|
status: error.response.status,
|
|
162
113
|
text: error.response.text,
|
|
163
114
|
});
|
|
@@ -174,7 +125,8 @@ class AttachmentsService extends common_1.BaseService {
|
|
|
174
125
|
if (!fs.existsSync(path)) {
|
|
175
126
|
throw new Error(`File not found: ${path}`);
|
|
176
127
|
}
|
|
177
|
-
|
|
128
|
+
logger_1.default.debug("[attachments] upload file", { path });
|
|
129
|
+
const id = yield (0, common_1.withHttpRetry)(() => __awaiter(this, void 0, void 0, function* () {
|
|
178
130
|
const fileStream = common_1.Utils.readStream(path);
|
|
179
131
|
try {
|
|
180
132
|
// @ts-ignore
|
|
@@ -185,13 +137,14 @@ class AttachmentsService extends common_1.BaseService {
|
|
|
185
137
|
finally {
|
|
186
138
|
fileStream.destroy();
|
|
187
139
|
}
|
|
188
|
-
}));
|
|
140
|
+
}), Object.assign(Object.assign({}, UPLOAD_RETRY_OPTIONS), { label: `uploadFile:${path}` }));
|
|
141
|
+
logger_1.default.debug("[attachments] upload file ok", { path, id });
|
|
189
142
|
attachmentIds.push(id);
|
|
190
143
|
}
|
|
191
144
|
catch (error) {
|
|
192
|
-
|
|
145
|
+
logger_1.default.error(`Error uploading attachment ${path}:`, error);
|
|
193
146
|
if (error.response) {
|
|
194
|
-
|
|
147
|
+
logger_1.default.error("Response details:", {
|
|
195
148
|
status: error.response.status,
|
|
196
149
|
text: error.response.text,
|
|
197
150
|
});
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.handleHttpError = handleHttpError;
|
|
7
|
+
const logger_1 = __importDefault(require("../../logger"));
|
|
4
8
|
function handleHttpError(err, message = "") {
|
|
5
|
-
|
|
9
|
+
logger_1.default.error(`HttpError ${err.statusCode}: ${message}. Error body: \n`, err.body);
|
|
6
10
|
}
|
|
@@ -6,8 +6,6 @@ export declare class AutotestsService extends BaseService implements IAutotestSe
|
|
|
6
6
|
protected readonly config: AdapterConfig;
|
|
7
7
|
protected _client: TestitApiClient.AutoTestsApi;
|
|
8
8
|
protected _converter: IAutotestConverter;
|
|
9
|
-
private MAX_TRIES;
|
|
10
|
-
private WAITING_TIME;
|
|
11
9
|
constructor(config: AdapterConfig);
|
|
12
10
|
createAutotest(autotest: AutotestPost): Promise<void>;
|
|
13
11
|
updateAutotest(autotest: AutotestPost): Promise<void>;
|
|
@@ -41,6 +41,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
41
41
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
42
42
|
});
|
|
43
43
|
};
|
|
44
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
45
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
46
|
+
};
|
|
44
47
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
48
|
exports.AutotestsService = void 0;
|
|
46
49
|
// @ts-ignore
|
|
@@ -48,12 +51,11 @@ const TestitApiClient = __importStar(require("testit-api-client"));
|
|
|
48
51
|
const common_1 = require("../../common");
|
|
49
52
|
const autotests_converter_1 = require("./autotests.converter");
|
|
50
53
|
const autotests_handler_1 = require("./autotests.handler");
|
|
54
|
+
const logger_1 = __importDefault(require("../../logger"));
|
|
51
55
|
class AutotestsService extends common_1.BaseService {
|
|
52
56
|
constructor(config) {
|
|
53
57
|
super(config);
|
|
54
58
|
this.config = config;
|
|
55
|
-
this.MAX_TRIES = 10;
|
|
56
|
-
this.WAITING_TIME = 100;
|
|
57
59
|
this._client = new TestitApiClient.AutoTestsApi();
|
|
58
60
|
this._converter = new autotests_converter_1.AutotestConverter(config);
|
|
59
61
|
}
|
|
@@ -61,9 +63,10 @@ class AutotestsService extends common_1.BaseService {
|
|
|
61
63
|
return __awaiter(this, void 0, void 0, function* () {
|
|
62
64
|
const autotestPost = this._converter.toOriginAutotest(autotest);
|
|
63
65
|
(0, common_1.escapeHtmlInObject)(autotestPost);
|
|
64
|
-
|
|
65
|
-
|
|
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
70
|
// @ts-ignore
|
|
68
71
|
.catch((err) => (0, autotests_handler_1.handleHttpError)(err, `Failed create autotest "${autotestPost.name}"`));
|
|
69
72
|
});
|
|
@@ -72,9 +75,10 @@ class AutotestsService extends common_1.BaseService {
|
|
|
72
75
|
return __awaiter(this, void 0, void 0, function* () {
|
|
73
76
|
const autotestPost = this._converter.toOriginAutotest(autotest);
|
|
74
77
|
(0, common_1.escapeHtmlInObject)(autotestPost);
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
.
|
|
78
|
+
logger_1.default.debug("[autotests] updateAutoTest", { externalId: autotest.externalId, name: autotest.name });
|
|
79
|
+
yield (0, common_1.withHttpRetry)(() => this._client
|
|
80
|
+
.updateAutoTest({ autoTestUpdateApiModel: autotestPost }), { label: "updateAutoTest" })
|
|
81
|
+
.then(() => logger_1.default.log(`Update autotest "${autotest.name}".`))
|
|
78
82
|
// @ts-ignore
|
|
79
83
|
.catch((err) => (0, autotests_handler_1.handleHttpError)(err, `Failed update autotest "${autotestPost.name}"`));
|
|
80
84
|
});
|
|
@@ -85,9 +89,11 @@ class AutotestsService extends common_1.BaseService {
|
|
|
85
89
|
var _a, _b;
|
|
86
90
|
const originAutotest = yield this.getAutotestByExternalId(autotest.externalId);
|
|
87
91
|
if (!originAutotest) {
|
|
92
|
+
logger_1.default.debug("[autotests] loadAutotest → create", { externalId: autotest.externalId, status });
|
|
88
93
|
yield this.createAutotest(autotest);
|
|
89
94
|
return;
|
|
90
95
|
}
|
|
96
|
+
logger_1.default.debug("[autotests] loadAutotest → update path", { externalId: autotest.externalId, status });
|
|
91
97
|
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 });
|
|
92
98
|
// fix the issue with lowercase status
|
|
93
99
|
const currentStatus = status.toLowerCase();
|
|
@@ -103,10 +109,10 @@ class AutotestsService extends common_1.BaseService {
|
|
|
103
109
|
yield this.updateAutotestFromFailed(originAutotest, mergedAutotest);
|
|
104
110
|
return;
|
|
105
111
|
}
|
|
106
|
-
|
|
112
|
+
logger_1.default.log(`Cannot update skipped autotest ${autotest.name} without name or externalId`);
|
|
107
113
|
return;
|
|
108
114
|
default:
|
|
109
|
-
|
|
115
|
+
logger_1.default.log(`Cannot update autotest ${autotest.name} with unknown status ${status}`);
|
|
110
116
|
}
|
|
111
117
|
});
|
|
112
118
|
}
|
|
@@ -119,35 +125,26 @@ class AutotestsService extends common_1.BaseService {
|
|
|
119
125
|
linkToWorkItems(internalId, workItemIds) {
|
|
120
126
|
return __awaiter(this, void 0, void 0, function* () {
|
|
121
127
|
const promises = workItemIds.map((workItemId) => __awaiter(this, void 0, void 0, function* () {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
129
|
-
catch (e) {
|
|
130
|
-
console.error(`Cannot link autotest ${internalId} to work item ${workItemId}`);
|
|
131
|
-
// console.error(e);
|
|
132
|
-
yield new Promise((f) => setTimeout(f, this.WAITING_TIME));
|
|
133
|
-
}
|
|
128
|
+
var _a, _b;
|
|
129
|
+
try {
|
|
130
|
+
yield this._client.linkAutoTestToWorkItem(internalId, { workItemIdApiModel: { id: workItemId } });
|
|
131
|
+
logger_1.default.log(`Link autotest ${internalId} to workitem ${workItemId} is successfully`);
|
|
132
|
+
}
|
|
133
|
+
catch (e) {
|
|
134
|
+
logger_1.default.error(`Cannot link autotest ${internalId} to work item ${workItemId}`, (_b = (_a = e === null || e === void 0 ? void 0 : e.body) !== null && _a !== void 0 ? _a : e === null || e === void 0 ? void 0 : e.error) !== null && _b !== void 0 ? _b : e);
|
|
134
135
|
}
|
|
135
136
|
}));
|
|
136
|
-
yield Promise.all(promises)
|
|
137
|
+
yield Promise.all(promises);
|
|
137
138
|
});
|
|
138
139
|
}
|
|
139
140
|
unlinkToWorkItem(internalId, workItemId) {
|
|
140
141
|
return __awaiter(this, void 0, void 0, function* () {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}
|
|
147
|
-
catch (e) {
|
|
148
|
-
console.log(`Cannot unlink autotest ${internalId} to work item ${workItemId}: ${e}`);
|
|
149
|
-
yield new Promise((f) => setTimeout(f, this.WAITING_TIME));
|
|
150
|
-
}
|
|
142
|
+
try {
|
|
143
|
+
yield this._client.deleteAutoTestLinkFromWorkItem(internalId, { workItemId: workItemId });
|
|
144
|
+
logger_1.default.log(`Unlink autotest ${internalId} from workitem ${workItemId} is successfully`);
|
|
145
|
+
}
|
|
146
|
+
catch (e) {
|
|
147
|
+
logger_1.default.log(`Cannot unlink autotest ${internalId} from work item ${workItemId}: ${e}`);
|
|
151
148
|
}
|
|
152
149
|
});
|
|
153
150
|
}
|
|
@@ -159,7 +156,7 @@ class AutotestsService extends common_1.BaseService {
|
|
|
159
156
|
.then((res) => res.body)
|
|
160
157
|
// @ts-ignore
|
|
161
158
|
.catch((e) => {
|
|
162
|
-
|
|
159
|
+
logger_1.default.log(`Cannot get linked workitems to autotest ${internalId}: ${e}`);
|
|
163
160
|
return [];
|
|
164
161
|
});
|
|
165
162
|
});
|
|
@@ -191,7 +188,7 @@ class AutotestsService extends common_1.BaseService {
|
|
|
191
188
|
return autotest ? this._converter.toLocalAutotest(autotest) : null;
|
|
192
189
|
})
|
|
193
190
|
.catch((reason) => {
|
|
194
|
-
|
|
191
|
+
logger_1.default.error(reason);
|
|
195
192
|
return null;
|
|
196
193
|
});
|
|
197
194
|
});
|
|
@@ -8,6 +8,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
11
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
15
|
exports.SyncStorageRunner = void 0;
|
|
13
16
|
const fs_1 = require("fs");
|
|
@@ -17,6 +20,7 @@ const path_1 = require("path");
|
|
|
17
20
|
const process_1 = require("process");
|
|
18
21
|
const child_process_1 = require("child_process");
|
|
19
22
|
const utils_1 = require("../../common/utils");
|
|
23
|
+
const logger_1 = __importDefault(require("../../logger"));
|
|
20
24
|
// Generated sync-storage client is bundled into lib/sync-storage/dist during build.
|
|
21
25
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
22
26
|
const SyncStorageClient = require("../../sync-storage/dist/index");
|
|
@@ -65,7 +69,7 @@ class SyncStorageRunner {
|
|
|
65
69
|
return true;
|
|
66
70
|
}
|
|
67
71
|
catch (error) {
|
|
68
|
-
|
|
72
|
+
logger_1.default.warn(`Sync storage start failed: ${error}`);
|
|
69
73
|
return false;
|
|
70
74
|
}
|
|
71
75
|
});
|
|
@@ -108,7 +112,7 @@ class SyncStorageRunner {
|
|
|
108
112
|
return __awaiter(this, void 0, void 0, function* () {
|
|
109
113
|
if (!this.running || !this.isMaster || this.alreadyInProgress || this.inProgressPublishing) {
|
|
110
114
|
if ((0, utils_1.isTmsLoadTestRunDebug)()) {
|
|
111
|
-
|
|
115
|
+
logger_1.default.debug("[syncstorage] skip in-progress cut publish", {
|
|
112
116
|
reason: {
|
|
113
117
|
notRunning: !this.running,
|
|
114
118
|
notMaster: !this.isMaster,
|
|
@@ -121,9 +125,9 @@ class SyncStorageRunner {
|
|
|
121
125
|
return false;
|
|
122
126
|
}
|
|
123
127
|
if (!model.projectId || !model.autoTestExternalId || !model.statusCode || !model.statusType) {
|
|
124
|
-
|
|
128
|
+
logger_1.default.warn("Sync storage in-progress payload is incomplete; skipping publish.");
|
|
125
129
|
if ((0, utils_1.isTmsLoadTestRunDebug)()) {
|
|
126
|
-
|
|
130
|
+
logger_1.default.debug("[syncstorage] incomplete in-progress cut payload", {
|
|
127
131
|
hasProjectId: Boolean(model.projectId),
|
|
128
132
|
hasAutoTestExternalId: Boolean(model.autoTestExternalId),
|
|
129
133
|
hasStatusCode: Boolean(model.statusCode),
|
|
@@ -144,11 +148,11 @@ class SyncStorageRunner {
|
|
|
144
148
|
yield this.withRetry(() => __awaiter(this, void 0, void 0, function* () { return this.testResultsApi.inProgressTestResultPost(this.testRunId, request); }), SyncStorageRunner.RETRY_COUNT);
|
|
145
149
|
this.alreadyInProgress = true;
|
|
146
150
|
if ((0, utils_1.isTmsLoadTestRunDebug)()) {
|
|
147
|
-
|
|
151
|
+
logger_1.default.debug("[syncstorage] alreadyInProgress set", {
|
|
148
152
|
workerPid: this.workerPid,
|
|
149
153
|
autoTestExternalId: model.autoTestExternalId,
|
|
150
154
|
});
|
|
151
|
-
|
|
155
|
+
logger_1.default.debug("[syncstorage] in-progress cut published", {
|
|
152
156
|
workerPid: this.workerPid,
|
|
153
157
|
autoTestExternalId: model.autoTestExternalId,
|
|
154
158
|
});
|
|
@@ -156,9 +160,9 @@ class SyncStorageRunner {
|
|
|
156
160
|
return true;
|
|
157
161
|
}
|
|
158
162
|
catch (error) {
|
|
159
|
-
|
|
163
|
+
logger_1.default.warn(`Sync storage in-progress publish failed: ${error}`);
|
|
160
164
|
if ((0, utils_1.isTmsLoadTestRunDebug)()) {
|
|
161
|
-
|
|
165
|
+
logger_1.default.debug("[syncstorage] in-progress cut publish failed", {
|
|
162
166
|
workerPid: this.workerPid,
|
|
163
167
|
autoTestExternalId: model.autoTestExternalId,
|
|
164
168
|
});
|
|
@@ -184,7 +188,7 @@ class SyncStorageRunner {
|
|
|
184
188
|
yield this.withRetry(() => __awaiter(this, void 0, void 0, function* () { return this.workersApi.setWorkerStatusPost(request); }), SyncStorageRunner.RETRY_COUNT);
|
|
185
189
|
}
|
|
186
190
|
catch (error) {
|
|
187
|
-
|
|
191
|
+
logger_1.default.warn(`Sync storage set worker status failed: ${error}`);
|
|
188
192
|
}
|
|
189
193
|
});
|
|
190
194
|
}
|
|
@@ -204,7 +208,7 @@ class SyncStorageRunner {
|
|
|
204
208
|
yield this.withRetry(() => __awaiter(this, void 0, void 0, function* () { return this.completionApi.forceCompletionGet(this.testRunId); }), SyncStorageRunner.RETRY_COUNT);
|
|
205
209
|
}
|
|
206
210
|
catch (error) {
|
|
207
|
-
|
|
211
|
+
logger_1.default.warn(`Sync storage completion failed: ${error}`);
|
|
208
212
|
}
|
|
209
213
|
});
|
|
210
214
|
}
|
|
@@ -241,7 +245,7 @@ class SyncStorageRunner {
|
|
|
241
245
|
return started;
|
|
242
246
|
}
|
|
243
247
|
catch (error) {
|
|
244
|
-
|
|
248
|
+
logger_1.default.warn(`Sync storage local process start failed: ${error}`);
|
|
245
249
|
return false;
|
|
246
250
|
}
|
|
247
251
|
});
|
|
@@ -349,7 +353,7 @@ class SyncStorageRunner {
|
|
|
349
353
|
}
|
|
350
354
|
}
|
|
351
355
|
exports.SyncStorageRunner = SyncStorageRunner;
|
|
352
|
-
SyncStorageRunner.VERSION = "v0.3.
|
|
356
|
+
SyncStorageRunner.VERSION = "v0.3.5";
|
|
353
357
|
SyncStorageRunner.STARTUP_TIMEOUT_MS = 30000;
|
|
354
358
|
SyncStorageRunner.STARTUP_POLL_MS = 1000;
|
|
355
359
|
SyncStorageRunner.PROCESS_WARMUP_MS = 2000;
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.handleHttpError = handleHttpError;
|
|
7
|
+
const logger_1 = __importDefault(require("../../logger"));
|
|
4
8
|
function handleHttpError(err, message = "") {
|
|
5
|
-
|
|
9
|
+
logger_1.default.error(`HttpError ${err.statusCode}: ${message}. Error body: \n`, err.body);
|
|
6
10
|
}
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.TestRunErrorHandler = void 0;
|
|
7
|
+
const logger_1 = __importDefault(require("../../logger"));
|
|
4
8
|
class TestRunErrorHandler {
|
|
5
9
|
static handleErrorStartTestRun(err, message = "") {
|
|
6
|
-
|
|
10
|
+
logger_1.default.error(`HttpError ${err.statusCode}. Failed start test run in system. Message: ${message}. Error body:\n`, err.body);
|
|
7
11
|
}
|
|
8
12
|
static handleErrorCompletedTestRun(err, message = "") {
|
|
9
|
-
|
|
13
|
+
logger_1.default.error(`HttpError ${err.statusCode}. Failed completed test run in system. Message: ${message}. Error body:\n`, err.body);
|
|
10
14
|
}
|
|
11
15
|
}
|
|
12
16
|
exports.TestRunErrorHandler = TestRunErrorHandler;
|
|
@@ -41,6 +41,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
41
41
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
42
42
|
});
|
|
43
43
|
};
|
|
44
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
45
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
46
|
+
};
|
|
44
47
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
48
|
exports.TestRunsService = void 0;
|
|
46
49
|
// @ts-ignore
|
|
@@ -49,6 +52,7 @@ const common_1 = require("../../common");
|
|
|
49
52
|
const utils_1 = require("../../common/utils");
|
|
50
53
|
const testruns_converter_1 = require("./testruns.converter");
|
|
51
54
|
const testruns_handler_1 = require("./testruns.handler");
|
|
55
|
+
const logger_1 = __importDefault(require("../../logger"));
|
|
52
56
|
class TestRunsService extends common_1.BaseService {
|
|
53
57
|
constructor(config) {
|
|
54
58
|
super(config);
|
|
@@ -66,7 +70,7 @@ class TestRunsService extends common_1.BaseService {
|
|
|
66
70
|
.createEmpty({ createEmptyTestRunApiModel: (0, utils_1.escapeHtmlInObject)(createRequest) })
|
|
67
71
|
// @ts-ignore
|
|
68
72
|
.then((response) => {
|
|
69
|
-
//
|
|
73
|
+
//logger.debug("Full response from createEmpty:", response);
|
|
70
74
|
const data = response.body || response;
|
|
71
75
|
if (!data) {
|
|
72
76
|
throw new Error("API returned undefined response");
|
|
@@ -77,7 +81,7 @@ class TestRunsService extends common_1.BaseService {
|
|
|
77
81
|
return data.id;
|
|
78
82
|
})
|
|
79
83
|
.catch((err) => {
|
|
80
|
-
|
|
84
|
+
logger_1.default.error("Error in createTestRun:", err);
|
|
81
85
|
throw err;
|
|
82
86
|
});
|
|
83
87
|
});
|
|
@@ -101,7 +105,7 @@ class TestRunsService extends common_1.BaseService {
|
|
|
101
105
|
.updateEmpty({ updateEmptyTestRunApiModel: testRun })
|
|
102
106
|
// @ts-ignore
|
|
103
107
|
.then((response) => {
|
|
104
|
-
|
|
108
|
+
logger_1.default.log("Full response from updateEmpty:", response);
|
|
105
109
|
const data = response.body || response;
|
|
106
110
|
if (!data) {
|
|
107
111
|
throw new Error("API returned undefined response");
|
|
@@ -149,7 +153,7 @@ class TestRunsService extends common_1.BaseService {
|
|
|
149
153
|
statusCode: model.statusCode,
|
|
150
154
|
hasStartedOn: Boolean(model.startedOn),
|
|
151
155
|
});
|
|
152
|
-
yield this.
|
|
156
|
+
yield this.sendAutotestResultWithRetry(testRunId, model);
|
|
153
157
|
(0, utils_1.logTmsLoadTestRun)("POST setAutoTestResults (InProgress stub) done", {
|
|
154
158
|
autoTestExternalId: model.autoTestExternalId,
|
|
155
159
|
});
|
|
@@ -171,7 +175,7 @@ class TestRunsService extends common_1.BaseService {
|
|
|
171
175
|
yield this.sendAutotestResultWithRetry(testRunId, autotestResult).catch((err) => {
|
|
172
176
|
var _a, _b;
|
|
173
177
|
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;
|
|
174
|
-
|
|
178
|
+
logger_1.default.error("[testit-js-commons:loadTestRun] FAILED to post final result", {
|
|
175
179
|
testRunId,
|
|
176
180
|
autoTestExternalId: autotestResult.autoTestExternalId,
|
|
177
181
|
error: normalized,
|
|
@@ -182,30 +186,18 @@ class TestRunsService extends common_1.BaseService {
|
|
|
182
186
|
}
|
|
183
187
|
sendAutotestResultWithRetry(testRunId, autotestResult) {
|
|
184
188
|
return __awaiter(this, void 0, void 0, function* () {
|
|
185
|
-
var _a
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
code === "ETIMEDOUT" ||
|
|
198
|
-
code === "EPIPE" ||
|
|
199
|
-
code === "ECONNABORTED" ||
|
|
200
|
-
msg.includes("socket hang up") ||
|
|
201
|
-
msg.includes("read ECONNRESET") ||
|
|
202
|
-
(typeof status === "number" && status >= 500);
|
|
203
|
-
if (!transient || attempt === maxAttempts) {
|
|
204
|
-
throw err;
|
|
205
|
-
}
|
|
206
|
-
yield new Promise((resolve) => setTimeout(resolve, 1000));
|
|
207
|
-
}
|
|
208
|
-
}
|
|
189
|
+
var _a;
|
|
190
|
+
yield (0, utils_1.withHttpRetry)(() => this._client.setAutoTestResultsForTestRun(testRunId, {
|
|
191
|
+
autoTestResultsForTestRunModel: [autotestResult],
|
|
192
|
+
}), {
|
|
193
|
+
label: `setAutoTestResults:${autotestResult.autoTestExternalId}:${(_a = autotestResult.statusCode) !== null && _a !== void 0 ? _a : autotestResult.statusType}`,
|
|
194
|
+
});
|
|
195
|
+
logger_1.default.debug("[testruns] setAutoTestResults ok", {
|
|
196
|
+
testRunId,
|
|
197
|
+
autoTestExternalId: autotestResult.autoTestExternalId,
|
|
198
|
+
statusCode: autotestResult.statusCode,
|
|
199
|
+
statusType: autotestResult.statusType,
|
|
200
|
+
});
|
|
209
201
|
});
|
|
210
202
|
}
|
|
211
203
|
}
|
|
@@ -8,11 +8,15 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
11
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
15
|
exports.BaseStrategy = void 0;
|
|
13
16
|
const client_1 = require("../client");
|
|
14
17
|
const utils_1 = require("../common/utils");
|
|
15
18
|
const syncstorage_1 = require("../services/syncstorage");
|
|
19
|
+
const logger_1 = __importDefault(require("../logger"));
|
|
16
20
|
class BaseStrategy {
|
|
17
21
|
constructor(config) {
|
|
18
22
|
this.config = config;
|
|
@@ -45,6 +49,14 @@ class BaseStrategy {
|
|
|
45
49
|
}
|
|
46
50
|
loadAutotest(autotest, status) {
|
|
47
51
|
return __awaiter(this, void 0, void 0, function* () {
|
|
52
|
+
var _a, _b, _c, _d, _e, _f;
|
|
53
|
+
logger_1.default.debug("[strategy] loadAutotest", {
|
|
54
|
+
externalId: autotest.externalId,
|
|
55
|
+
status,
|
|
56
|
+
setupSteps: (_b = (_a = autotest.setup) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0,
|
|
57
|
+
testSteps: (_d = (_c = autotest.steps) === null || _c === void 0 ? void 0 : _c.length) !== null && _d !== void 0 ? _d : 0,
|
|
58
|
+
teardownSteps: (_f = (_e = autotest.teardown) === null || _e === void 0 ? void 0 : _e.length) !== null && _f !== void 0 ? _f : 0,
|
|
59
|
+
});
|
|
48
60
|
yield this.client.autoTests.loadAutotest(autotest, status);
|
|
49
61
|
if (Array.isArray(autotest.workItemIds)) {
|
|
50
62
|
yield this.updateTestLinkToWorkItems(autotest.externalId, autotest.workItemIds);
|
|
@@ -75,7 +87,7 @@ class BaseStrategy {
|
|
|
75
87
|
}
|
|
76
88
|
}
|
|
77
89
|
yield this.client.autoTests.linkToWorkItems(existingAutotest, workItemIds).catch((err) => {
|
|
78
|
-
|
|
90
|
+
logger_1.default.log("Failed link work items. \n", err);
|
|
79
91
|
});
|
|
80
92
|
});
|
|
81
93
|
}
|