@copado/agentia-cli 0.29.0-alpha.0 → 0.31.0-alpha.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/README.md +918 -54
- package/dist/commands/auth/get.d.ts +2 -0
- package/dist/commands/auth/get.js +13 -2
- package/dist/commands/auth/get.js.map +1 -1
- package/dist/commands/auth/set.d.ts +6 -0
- package/dist/commands/auth/set.js +43 -4
- package/dist/commands/auth/set.js.map +1 -1
- package/dist/commands/testing/build/list.d.ts +16 -0
- package/dist/commands/testing/build/list.js +47 -0
- package/dist/commands/testing/build/list.js.map +1 -0
- package/dist/commands/testing/build/logs.d.ts +21 -0
- package/dist/commands/testing/build/logs.js +42 -0
- package/dist/commands/testing/build/logs.js.map +1 -0
- package/dist/commands/testing/build/run.d.ts +19 -0
- package/dist/commands/testing/build/run.js +39 -0
- package/dist/commands/testing/build/run.js.map +1 -0
- package/dist/commands/testing/create.d.ts +18 -0
- package/dist/commands/testing/create.js +46 -0
- package/dist/commands/testing/create.js.map +1 -0
- package/dist/commands/testing/job/create.d.ts +18 -0
- package/dist/commands/testing/job/create.js +46 -0
- package/dist/commands/testing/job/create.js.map +1 -0
- package/dist/commands/testing/job/download.d.ts +22 -0
- package/dist/commands/testing/job/download.js +56 -0
- package/dist/commands/testing/job/download.js.map +1 -0
- package/dist/commands/testing/job/files.d.ts +19 -0
- package/dist/commands/testing/job/files.js +35 -0
- package/dist/commands/testing/job/files.js.map +1 -0
- package/dist/commands/testing/job/list.d.ts +14 -0
- package/dist/commands/testing/job/list.js +32 -0
- package/dist/commands/testing/job/list.js.map +1 -0
- package/dist/commands/testing/job/upload.d.ts +29 -0
- package/dist/commands/testing/job/upload.js +57 -0
- package/dist/commands/testing/job/upload.js.map +1 -0
- package/dist/commands/testing/logs.d.ts +21 -0
- package/dist/commands/testing/logs.js +42 -0
- package/dist/commands/testing/logs.js.map +1 -0
- package/dist/commands/testing/project/create.d.ts +14 -0
- package/dist/commands/testing/project/create.js +36 -0
- package/dist/commands/testing/project/create.js.map +1 -0
- package/dist/commands/testing/project/list.d.ts +13 -0
- package/dist/commands/testing/project/list.js +31 -0
- package/dist/commands/testing/project/list.js.map +1 -0
- package/dist/commands/testing/robot/create.d.ts +19 -0
- package/dist/commands/testing/robot/create.js +60 -0
- package/dist/commands/testing/robot/create.js.map +1 -0
- package/dist/commands/testing/robot/list.d.ts +13 -0
- package/dist/commands/testing/robot/list.js +28 -0
- package/dist/commands/testing/robot/list.js.map +1 -0
- package/dist/commands/testing/run.d.ts +19 -0
- package/dist/commands/testing/run.js +32 -0
- package/dist/commands/testing/run.js.map +1 -0
- package/dist/commands/testing/status.d.ts +16 -0
- package/dist/commands/testing/status.js +47 -0
- package/dist/commands/testing/status.js.map +1 -0
- package/dist/commands/testing/test/create.d.ts +1 -0
- package/dist/commands/testing/test/create.js +2 -0
- package/dist/commands/testing/test/create.js.map +1 -0
- package/dist/commands/testing/test/logs.d.ts +1 -0
- package/dist/commands/testing/test/logs.js +2 -0
- package/dist/commands/testing/test/logs.js.map +1 -0
- package/dist/commands/testing/test/run.d.ts +1 -0
- package/dist/commands/testing/test/run.js +2 -0
- package/dist/commands/testing/test/run.js.map +1 -0
- package/dist/commands/testing/test/status.d.ts +1 -0
- package/dist/commands/testing/test/status.js +2 -0
- package/dist/commands/testing/test/status.js.map +1 -0
- package/dist/commands/testing/test/update.d.ts +1 -0
- package/dist/commands/testing/test/update.js +2 -0
- package/dist/commands/testing/test/update.js.map +1 -0
- package/dist/commands/testing/update.d.ts +29 -0
- package/dist/commands/testing/update.js +57 -0
- package/dist/commands/testing/update.js.map +1 -0
- package/dist/commands/testing/variable/create.d.ts +20 -0
- package/dist/commands/testing/variable/create.js +65 -0
- package/dist/commands/testing/variable/create.js.map +1 -0
- package/dist/commands/testing/variable/delete.d.ts +19 -0
- package/dist/commands/testing/variable/delete.js +30 -0
- package/dist/commands/testing/variable/delete.js.map +1 -0
- package/dist/commands/testing/variable/list.d.ts +15 -0
- package/dist/commands/testing/variable/list.js +40 -0
- package/dist/commands/testing/variable/list.js.map +1 -0
- package/dist/lib/credential-store.d.ts +4 -0
- package/dist/lib/credential-store.js +14 -0
- package/dist/lib/credential-store.js.map +1 -1
- package/dist/lib/mcp/testing-tools.d.ts +144 -0
- package/dist/lib/mcp/testing-tools.js +366 -0
- package/dist/lib/mcp/testing-tools.js.map +1 -0
- package/dist/lib/mcp/tools.d.ts +268 -0
- package/dist/lib/mcp/tools.js +3 -0
- package/dist/lib/mcp/tools.js.map +1 -1
- package/dist/lib/testing-config.d.ts +17 -0
- package/dist/lib/testing-config.js +68 -0
- package/dist/lib/testing-config.js.map +1 -0
- package/dist/lib/testing-flags.d.ts +33 -0
- package/dist/lib/testing-flags.js +70 -0
- package/dist/lib/testing-flags.js.map +1 -0
- package/dist/lib/testing-gateway.d.ts +33 -0
- package/dist/lib/testing-gateway.js +161 -0
- package/dist/lib/testing-gateway.js.map +1 -0
- package/dist/lib/testing-operations.d.ts +99 -0
- package/dist/lib/testing-operations.js +268 -0
- package/dist/lib/testing-operations.js.map +1 -0
- package/dist/lib/testing-types.d.ts +138 -0
- package/dist/lib/testing-types.js +190 -0
- package/dist/lib/testing-types.js.map +1 -0
- package/oclif.manifest.json +2323 -85
- package/package.json +4 -1
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
export const testingConnectionFlags = {
|
|
3
|
+
domain: Flags.string({
|
|
4
|
+
description: 'CRT domain hostname. Defaults to COPADO_ROBOTIC_DOMAIN, CRT_DOMAIN, or stored auth config.',
|
|
5
|
+
}),
|
|
6
|
+
org: Flags.integer({
|
|
7
|
+
description: 'CRT organization ID. Defaults to COPADO_ROBOTIC_ORG_ID, CRT_ORG, or stored auth config.',
|
|
8
|
+
}),
|
|
9
|
+
pat: Flags.string({
|
|
10
|
+
description: 'CRT PAT token override. Prefer auth set --crt or COPADO_ROBOTIC_API_KEY for regular use.',
|
|
11
|
+
}),
|
|
12
|
+
};
|
|
13
|
+
export const testingProjectFlag = {
|
|
14
|
+
project: Flags.integer({
|
|
15
|
+
char: 'p',
|
|
16
|
+
description: 'CRT project ID.',
|
|
17
|
+
required: true,
|
|
18
|
+
}),
|
|
19
|
+
};
|
|
20
|
+
export const testingJobFlag = {
|
|
21
|
+
job: Flags.integer({
|
|
22
|
+
char: 'j',
|
|
23
|
+
description: 'CRT job/test ID.',
|
|
24
|
+
required: true,
|
|
25
|
+
}),
|
|
26
|
+
};
|
|
27
|
+
export const testingBranchFlag = {
|
|
28
|
+
branch: Flags.string({
|
|
29
|
+
description: 'Optional CRT branch name for file operations.',
|
|
30
|
+
}),
|
|
31
|
+
};
|
|
32
|
+
export const testingOutputFlags = {
|
|
33
|
+
output: Flags.string({
|
|
34
|
+
char: 'o',
|
|
35
|
+
description: 'Write raw output to this file path.',
|
|
36
|
+
}),
|
|
37
|
+
};
|
|
38
|
+
export const testingFileInputFlags = {
|
|
39
|
+
dir: Flags.string({
|
|
40
|
+
description: 'Directory containing Robot Framework files to upload. Can be provided multiple times.',
|
|
41
|
+
multiple: true,
|
|
42
|
+
}),
|
|
43
|
+
file: Flags.string({
|
|
44
|
+
char: 'f',
|
|
45
|
+
description: 'Robot Framework file to upload. Can be provided multiple times.',
|
|
46
|
+
multiple: true,
|
|
47
|
+
}),
|
|
48
|
+
};
|
|
49
|
+
export const testingUploadOperationFlags = {
|
|
50
|
+
add: Flags.string({
|
|
51
|
+
description: 'Add a file using local[:remote] syntax. Can be provided multiple times.',
|
|
52
|
+
multiple: true,
|
|
53
|
+
}),
|
|
54
|
+
remove: Flags.string({
|
|
55
|
+
description: 'Remove a remote CRT file path. Can be provided multiple times.',
|
|
56
|
+
multiple: true,
|
|
57
|
+
}),
|
|
58
|
+
replace: Flags.string({
|
|
59
|
+
description: 'Replace a file using local[:remote] syntax. Can be provided multiple times.',
|
|
60
|
+
multiple: true,
|
|
61
|
+
}),
|
|
62
|
+
};
|
|
63
|
+
export function testingConnectionOptions(flags) {
|
|
64
|
+
return {
|
|
65
|
+
domain: flags.domain,
|
|
66
|
+
org: flags.org,
|
|
67
|
+
pat: flags.pat,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=testing-flags.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testing-flags.js","sourceRoot":"","sources":["../../src/lib/testing-flags.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,KAAK,EAAC,MAAM,aAAa,CAAA;AAIjC,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC;QACnB,WAAW,EAAE,4FAA4F;KAC1G,CAAC;IACF,GAAG,EAAE,KAAK,CAAC,OAAO,CAAC;QACjB,WAAW,EAAE,yFAAyF;KACvG,CAAC;IACF,GAAG,EAAE,KAAK,CAAC,MAAM,CAAC;QAChB,WAAW,EAAE,0FAA0F;KACxG,CAAC;CACH,CAAA;AAED,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC;QACrB,IAAI,EAAE,GAAG;QACT,WAAW,EAAE,iBAAiB;QAC9B,QAAQ,EAAE,IAAI;KACf,CAAC;CACH,CAAA;AAED,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,GAAG,EAAE,KAAK,CAAC,OAAO,CAAC;QACjB,IAAI,EAAE,GAAG;QACT,WAAW,EAAE,kBAAkB;QAC/B,QAAQ,EAAE,IAAI;KACf,CAAC;CACH,CAAA;AAED,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC;QACnB,WAAW,EAAE,+CAA+C;KAC7D,CAAC;CACH,CAAA;AAED,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC;QACnB,IAAI,EAAE,GAAG;QACT,WAAW,EAAE,qCAAqC;KACnD,CAAC;CACH,CAAA;AAED,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,GAAG,EAAE,KAAK,CAAC,MAAM,CAAC;QAChB,WAAW,EAAE,uFAAuF;QACpG,QAAQ,EAAE,IAAI;KACf,CAAC;IACF,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC;QACjB,IAAI,EAAE,GAAG;QACT,WAAW,EAAE,iEAAiE;QAC9E,QAAQ,EAAE,IAAI;KACf,CAAC;CACH,CAAA;AAED,MAAM,CAAC,MAAM,2BAA2B,GAAG;IACzC,GAAG,EAAE,KAAK,CAAC,MAAM,CAAC;QAChB,WAAW,EAAE,yEAAyE;QACtF,QAAQ,EAAE,IAAI;KACf,CAAC;IACF,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC;QACnB,WAAW,EAAE,gEAAgE;QAC7E,QAAQ,EAAE,IAAI;KACf,CAAC;IACF,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC;QACpB,WAAW,EAAE,6EAA6E;QAC1F,QAAQ,EAAE,IAAI;KACf,CAAC;CACH,CAAA;AAQD,MAAM,UAAU,wBAAwB,CAAC,KAAkC;IACzE,OAAO;QACL,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,GAAG,EAAE,KAAK,CAAC,GAAG;QACd,GAAG,EAAE,KAAK,CAAC,GAAG;KACf,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { URL } from 'node:url';
|
|
2
|
+
import { type TestingConnectionOverrides } from './testing-config.js';
|
|
3
|
+
export declare const TESTING_DEFAULT_TIMEOUT_MS = 45000;
|
|
4
|
+
export type TestingRequest = {
|
|
5
|
+
body?: unknown;
|
|
6
|
+
endpoint: string;
|
|
7
|
+
method: 'DELETE' | 'GET' | 'PATCH' | 'POST' | 'PUT';
|
|
8
|
+
responseType?: 'json' | 'text';
|
|
9
|
+
service: 'pace' | 'platform';
|
|
10
|
+
};
|
|
11
|
+
export type TestingHttpRequestFn = (url: URL, method: string, headers: Record<string, string>, body: unknown, timeoutMs: number) => Promise<string>;
|
|
12
|
+
export type TestingGatewayCallOptions = TestingConnectionOverrides & {
|
|
13
|
+
timeoutMs?: number;
|
|
14
|
+
};
|
|
15
|
+
export declare class TestingGatewayError extends Error {
|
|
16
|
+
readonly requestMethod?: string;
|
|
17
|
+
readonly requestUrl?: string;
|
|
18
|
+
readonly retryAfterSeconds?: number;
|
|
19
|
+
readonly statusCode?: number;
|
|
20
|
+
constructor(message: string, options?: {
|
|
21
|
+
request?: {
|
|
22
|
+
method?: string;
|
|
23
|
+
url?: string;
|
|
24
|
+
};
|
|
25
|
+
retryAfterSeconds?: number;
|
|
26
|
+
statusCode?: number;
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
export declare class TestingGateway {
|
|
30
|
+
static call<T = unknown>(request: TestingRequest, options?: TestingGatewayCallOptions): Promise<T>;
|
|
31
|
+
}
|
|
32
|
+
export declare function setTestingHttpRequestForTests(requestFn: TestingHttpRequestFn): void;
|
|
33
|
+
export declare function resetTestingHttpRequestForTests(): void;
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import * as http from 'node:http';
|
|
2
|
+
import * as https from 'node:https';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
4
|
+
import { URL } from 'node:url';
|
|
5
|
+
import { createTransactionId, getGatewayTransactionId } from './cicd-gateway.js';
|
|
6
|
+
import { paceBaseUrl, platformBaseUrl, resolveTestingCredentials, } from './testing-config.js';
|
|
7
|
+
const require = createRequire(import.meta.url);
|
|
8
|
+
const packageJson = require('../../package.json');
|
|
9
|
+
export const TESTING_DEFAULT_TIMEOUT_MS = 45_000;
|
|
10
|
+
export class TestingGatewayError extends Error {
|
|
11
|
+
requestMethod;
|
|
12
|
+
requestUrl;
|
|
13
|
+
retryAfterSeconds;
|
|
14
|
+
statusCode;
|
|
15
|
+
constructor(message, options = {}) {
|
|
16
|
+
super(message);
|
|
17
|
+
this.name = 'TestingGatewayError';
|
|
18
|
+
this.requestMethod = options.request?.method;
|
|
19
|
+
this.requestUrl = options.request?.url;
|
|
20
|
+
this.retryAfterSeconds = options.retryAfterSeconds;
|
|
21
|
+
this.statusCode = options.statusCode;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function userAgent() {
|
|
25
|
+
return `agentia-cli/${packageJson.version}`;
|
|
26
|
+
}
|
|
27
|
+
function transactionIdForRequest() {
|
|
28
|
+
return getGatewayTransactionId() ?? createTransactionId();
|
|
29
|
+
}
|
|
30
|
+
function parseRetryAfterHeader(value) {
|
|
31
|
+
if (value === undefined)
|
|
32
|
+
return undefined;
|
|
33
|
+
const raw = Array.isArray(value) ? value[0] : value;
|
|
34
|
+
if (raw === undefined)
|
|
35
|
+
return undefined;
|
|
36
|
+
if (typeof raw === 'number')
|
|
37
|
+
return Number.isFinite(raw) && raw >= 0 ? Math.ceil(raw) : undefined;
|
|
38
|
+
const trimmed = raw.trim();
|
|
39
|
+
if (/^\d+$/u.test(trimmed))
|
|
40
|
+
return Number.parseInt(trimmed, 10);
|
|
41
|
+
const dateMs = Date.parse(trimmed);
|
|
42
|
+
return Number.isNaN(dateMs) ? undefined : Math.max(0, Math.ceil((dateMs - Date.now()) / 1000));
|
|
43
|
+
}
|
|
44
|
+
function extractErrorMessage(raw) {
|
|
45
|
+
try {
|
|
46
|
+
const parsed = JSON.parse(raw);
|
|
47
|
+
for (const value of [parsed.message, parsed.error, parsed.title]) {
|
|
48
|
+
if (typeof value === 'string' && value.trim().length > 0)
|
|
49
|
+
return value.trim();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return raw.trim().length > 0 ? raw.trim() : undefined;
|
|
54
|
+
}
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
function defaultHttpsRequest(url, method, headers, body, timeoutMs) {
|
|
58
|
+
const hasBody = body !== undefined;
|
|
59
|
+
const payload = hasBody ? JSON.stringify(body) : '';
|
|
60
|
+
const requestHeaders = {
|
|
61
|
+
Accept: 'application/json',
|
|
62
|
+
...headers,
|
|
63
|
+
};
|
|
64
|
+
if (hasBody) {
|
|
65
|
+
requestHeaders['Content-Type'] = requestHeaders['Content-Type'] ?? 'application/json';
|
|
66
|
+
requestHeaders['Content-Length'] = Buffer.byteLength(payload).toString();
|
|
67
|
+
}
|
|
68
|
+
const transport = url.protocol === 'http:' ? http : https;
|
|
69
|
+
return new Promise((resolve, reject) => {
|
|
70
|
+
const req = transport.request({
|
|
71
|
+
headers: requestHeaders,
|
|
72
|
+
hostname: url.hostname,
|
|
73
|
+
method,
|
|
74
|
+
path: `${url.pathname}${url.search}`,
|
|
75
|
+
port: url.port.length > 0 ? Number(url.port) : undefined,
|
|
76
|
+
timeout: timeoutMs,
|
|
77
|
+
}, (res) => {
|
|
78
|
+
const chunks = [];
|
|
79
|
+
res.on('data', (chunk) => chunks.push(chunk));
|
|
80
|
+
res.on('end', () => {
|
|
81
|
+
const raw = Buffer.concat(chunks).toString('utf8');
|
|
82
|
+
const status = res.statusCode ?? 0;
|
|
83
|
+
if (status >= 400) {
|
|
84
|
+
const message = extractErrorMessage(raw) ?? `CRT request failed (${status}).`;
|
|
85
|
+
reject(new TestingGatewayError(message, {
|
|
86
|
+
retryAfterSeconds: status === 429 ? parseRetryAfterHeader(res.headers['retry-after']) : undefined,
|
|
87
|
+
statusCode: status,
|
|
88
|
+
}));
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
resolve(raw);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
req.on('error', (error) => reject(error));
|
|
95
|
+
req.on('timeout', () => {
|
|
96
|
+
req.destroy(new Error(`CRT request timed out after ${Math.ceil(timeoutMs / 1000)} seconds.`));
|
|
97
|
+
});
|
|
98
|
+
if (hasBody)
|
|
99
|
+
req.write(payload);
|
|
100
|
+
req.end();
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
let httpRequestFn = defaultHttpsRequest;
|
|
104
|
+
function requestUrl(request, domain) {
|
|
105
|
+
const baseUrl = request.service === 'platform' ? platformBaseUrl(domain) : paceBaseUrl(domain);
|
|
106
|
+
return new URL(request.endpoint, `${baseUrl}/`);
|
|
107
|
+
}
|
|
108
|
+
function withRequestSuffix(message, request) {
|
|
109
|
+
const suffix = request.url === undefined ? '' : ` Request: ${request.method ?? ''} ${request.url}`.trimEnd();
|
|
110
|
+
if (suffix.length === 0)
|
|
111
|
+
return message;
|
|
112
|
+
return message.endsWith('.') ? `${message} ${suffix}` : `${message}. ${suffix}`;
|
|
113
|
+
}
|
|
114
|
+
async function executeTestingRequest(request, options = {}) {
|
|
115
|
+
const credentials = resolveTestingCredentials(options);
|
|
116
|
+
const url = requestUrl(request, credentials.domain);
|
|
117
|
+
const requestInfo = { method: request.method, url: url.toString() };
|
|
118
|
+
const headers = {
|
|
119
|
+
'User-Agent': userAgent(),
|
|
120
|
+
'X-Authorization': credentials.pat,
|
|
121
|
+
'x-transaction-id': transactionIdForRequest(),
|
|
122
|
+
};
|
|
123
|
+
let raw;
|
|
124
|
+
try {
|
|
125
|
+
raw = await httpRequestFn(url, request.method, headers, request.body, options.timeoutMs ?? TESTING_DEFAULT_TIMEOUT_MS);
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
if (error instanceof TestingGatewayError) {
|
|
129
|
+
throw new TestingGatewayError(withRequestSuffix(error.message, requestInfo), {
|
|
130
|
+
request: requestInfo,
|
|
131
|
+
retryAfterSeconds: error.retryAfterSeconds,
|
|
132
|
+
statusCode: error.statusCode,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
throw error;
|
|
136
|
+
}
|
|
137
|
+
if (raw.trim().length === 0)
|
|
138
|
+
return undefined;
|
|
139
|
+
if (request.responseType === 'text')
|
|
140
|
+
return raw;
|
|
141
|
+
try {
|
|
142
|
+
return JSON.parse(raw);
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
throw new TestingGatewayError(withRequestSuffix(`CRT API returned non-JSON response: ${raw}`, requestInfo), {
|
|
146
|
+
request: requestInfo,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
export class TestingGateway {
|
|
151
|
+
static async call(request, options = {}) {
|
|
152
|
+
return executeTestingRequest(request, options);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
export function setTestingHttpRequestForTests(requestFn) {
|
|
156
|
+
httpRequestFn = requestFn;
|
|
157
|
+
}
|
|
158
|
+
export function resetTestingHttpRequestForTests() {
|
|
159
|
+
httpRequestFn = defaultHttpsRequest;
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=testing-gateway.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testing-gateway.js","sourceRoot":"","sources":["../../src/lib/testing-gateway.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AACjC,OAAO,KAAK,KAAK,MAAM,YAAY,CAAA;AACnC,OAAO,EAAC,aAAa,EAAC,MAAM,aAAa,CAAA;AACzC,OAAO,EAAC,GAAG,EAAC,MAAM,UAAU,CAAA;AAE5B,OAAO,EAAC,mBAAmB,EAAE,uBAAuB,EAAC,MAAM,mBAAmB,CAAA;AAC9E,OAAO,EACL,WAAW,EACX,eAAe,EACf,yBAAyB,GAE1B,MAAM,qBAAqB,CAAA;AAE5B,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAC9C,MAAM,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAsB,CAAA;AAEtE,MAAM,CAAC,MAAM,0BAA0B,GAAG,MAAM,CAAA;AAsBhD,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAC5B,aAAa,CAAS;IACtB,UAAU,CAAS;IACnB,iBAAiB,CAAS;IAC1B,UAAU,CAAS;IAEnC,YACE,OAAe,EACf,UAII,EAAE;QAEN,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAA;QACjC,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,OAAO,EAAE,MAAM,CAAA;QAC5C,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,OAAO,EAAE,GAAG,CAAA;QACtC,IAAI,CAAC,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAA;QAClD,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAA;IACtC,CAAC;CACF;AAED,SAAS,SAAS;IAChB,OAAO,eAAe,WAAW,CAAC,OAAO,EAAE,CAAA;AAC7C,CAAC;AAED,SAAS,uBAAuB;IAC9B,OAAO,uBAAuB,EAAE,IAAI,mBAAmB,EAAE,CAAA;AAC3D,CAAC;AAED,SAAS,qBAAqB,CAAC,KAAkC;IAC/D,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAA;IACzC,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;IACnD,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,SAAS,CAAA;IACvC,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IACjG,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAA;IAC1B,IAAI,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAA;IAC/D,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IAClC,OAAO,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAA;AAChG,CAAC;AAED,SAAS,mBAAmB,CAAC,GAAW;IACtC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA0D,CAAA;QACvF,KAAK,MAAM,KAAK,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;YACjE,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;gBAAE,OAAO,KAAK,CAAC,IAAI,EAAE,CAAA;QAC/E,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAA;IACvD,CAAC;IAED,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,SAAS,mBAAmB,CAC1B,GAAQ,EACR,MAAc,EACd,OAA+B,EAC/B,IAAa,EACb,SAAiB;IAEjB,MAAM,OAAO,GAAG,IAAI,KAAK,SAAS,CAAA;IAClC,MAAM,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;IACnD,MAAM,cAAc,GAA2B;QAC7C,MAAM,EAAE,kBAAkB;QAC1B,GAAG,OAAO;KACX,CAAA;IAED,IAAI,OAAO,EAAE,CAAC;QACZ,cAAc,CAAC,cAAc,CAAC,GAAG,cAAc,CAAC,cAAc,CAAC,IAAI,kBAAkB,CAAA;QACrF,cAAc,CAAC,gBAAgB,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAA;IAC1E,CAAC;IAED,MAAM,SAAS,GAAG,GAAG,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAA;IAEzD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,GAAG,GAAG,SAAS,CAAC,OAAO,CAC3B;YACE,OAAO,EAAE,cAAc;YACvB,QAAQ,EAAE,GAAG,CAAC,QAAQ;YACtB,MAAM;YACN,IAAI,EAAE,GAAG,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,MAAM,EAAE;YACpC,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;YACxD,OAAO,EAAE,SAAS;SACnB,EACD,CAAC,GAAG,EAAE,EAAE;YACN,MAAM,MAAM,GAAa,EAAE,CAAA;YAC3B,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAA;YACrD,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;gBACjB,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;gBAClD,MAAM,MAAM,GAAG,GAAG,CAAC,UAAU,IAAI,CAAC,CAAA;gBAElC,IAAI,MAAM,IAAI,GAAG,EAAE,CAAC;oBAClB,MAAM,OAAO,GAAG,mBAAmB,CAAC,GAAG,CAAC,IAAI,uBAAuB,MAAM,IAAI,CAAA;oBAC7E,MAAM,CACJ,IAAI,mBAAmB,CAAC,OAAO,EAAE;wBAC/B,iBAAiB,EAAE,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,qBAAqB,CAAC,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS;wBACjG,UAAU,EAAE,MAAM;qBACnB,CAAC,CACH,CAAA;oBACD,OAAM;gBACR,CAAC;gBAED,OAAO,CAAC,GAAG,CAAC,CAAA;YACd,CAAC,CAAC,CAAA;QACJ,CAAC,CACF,CAAA;QAED,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;QACzC,GAAG,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;YACrB,GAAG,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,+BAA+B,IAAI,CAAC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC,CAAA;QAC/F,CAAC,CAAC,CAAA;QAEF,IAAI,OAAO;YAAE,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;QAC/B,GAAG,CAAC,GAAG,EAAE,CAAA;IACX,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,IAAI,aAAa,GAAyB,mBAAmB,CAAA;AAE7D,SAAS,UAAU,CAAC,OAAuB,EAAE,MAAc;IACzD,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,CAAA;IAC9F,OAAO,IAAI,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,OAAO,GAAG,CAAC,CAAA;AACjD,CAAC;AAED,SAAS,iBAAiB,CAAC,OAAe,EAAE,OAAwC;IAClF,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,aAAa,OAAO,CAAC,MAAM,IAAI,EAAE,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAA;IAC5G,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,OAAO,CAAA;IACvC,OAAO,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,IAAI,MAAM,EAAE,CAAC,CAAC,CAAC,GAAG,OAAO,KAAK,MAAM,EAAE,CAAA;AACjF,CAAC;AAED,KAAK,UAAU,qBAAqB,CAAI,OAAuB,EAAE,UAAqC,EAAE;IACtG,MAAM,WAAW,GAAG,yBAAyB,CAAC,OAAO,CAAC,CAAA;IACtD,MAAM,GAAG,GAAG,UAAU,CAAC,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IACnD,MAAM,WAAW,GAAG,EAAC,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,QAAQ,EAAE,EAAC,CAAA;IACjE,MAAM,OAAO,GAAG;QACd,YAAY,EAAE,SAAS,EAAE;QACzB,iBAAiB,EAAE,WAAW,CAAC,GAAG;QAClC,kBAAkB,EAAE,uBAAuB,EAAE;KAC9C,CAAA;IAED,IAAI,GAAW,CAAA;IACf,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,aAAa,CAAC,GAAG,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,SAAS,IAAI,0BAA0B,CAAC,CAAA;IACxH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,mBAAmB,EAAE,CAAC;YACzC,MAAM,IAAI,mBAAmB,CAAC,iBAAiB,CAAC,KAAK,CAAC,OAAO,EAAE,WAAW,CAAC,EAAE;gBAC3E,OAAO,EAAE,WAAW;gBACpB,iBAAiB,EAAE,KAAK,CAAC,iBAAiB;gBAC1C,UAAU,EAAE,KAAK,CAAC,UAAU;aAC7B,CAAC,CAAA;QACJ,CAAC;QAED,MAAM,KAAK,CAAA;IACb,CAAC;IAED,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAc,CAAA;IAClD,IAAI,OAAO,CAAC,YAAY,KAAK,MAAM;QAAE,OAAO,GAAQ,CAAA;IAEpD,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAM,CAAA;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,mBAAmB,CAAC,iBAAiB,CAAC,uCAAuC,GAAG,EAAE,EAAE,WAAW,CAAC,EAAE;YAC1G,OAAO,EAAE,WAAW;SACrB,CAAC,CAAA;IACJ,CAAC;AACH,CAAC;AAED,MAAM,OAAO,cAAc;IAClB,MAAM,CAAC,KAAK,CAAC,IAAI,CACtB,OAAuB,EACvB,UAAqC,EAAE;QAEvC,OAAO,qBAAqB,CAAI,OAAO,EAAE,OAAO,CAAC,CAAA;IACnD,CAAC;CACF;AAED,MAAM,UAAU,6BAA6B,CAAC,SAA+B;IAC3E,aAAa,GAAG,SAAS,CAAA;AAC3B,CAAC;AAED,MAAM,UAAU,+BAA+B;IAC7C,aAAa,GAAG,mBAAmB,CAAA;AACrC,CAAC"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { type TestingGatewayCallOptions } from './testing-gateway.js';
|
|
2
|
+
import { type TestingAuthor, type TestingBuild, type TestingBuildRunResult, type TestingBuildStatus, type TestingCreateVariableRequest, type TestingDownloadResult, type TestingFileEntry, type TestingFileOperation, type TestingJob, type TestingJobCreateResult, type TestingLogResult, type TestingProject, type TestingRecord, type TestingRobot, type TestingVariable } from './testing-types.js';
|
|
3
|
+
export declare const DEFAULT_TESTING_AUTHOR: TestingAuthor;
|
|
4
|
+
export type TestingFileCollectionInput = {
|
|
5
|
+
dirs?: string[];
|
|
6
|
+
files?: string[];
|
|
7
|
+
};
|
|
8
|
+
export type TestingConnectionOptions = TestingGatewayCallOptions;
|
|
9
|
+
export type TestingCreateJobOptions = TestingConnectionOptions & {
|
|
10
|
+
description?: string;
|
|
11
|
+
fileInput?: TestingFileCollectionInput;
|
|
12
|
+
name: string;
|
|
13
|
+
projectId: number;
|
|
14
|
+
robotId: number | string;
|
|
15
|
+
};
|
|
16
|
+
export type TestingUploadOptions = TestingConnectionOptions & {
|
|
17
|
+
author?: TestingAuthor;
|
|
18
|
+
branch?: string;
|
|
19
|
+
jobId: number;
|
|
20
|
+
operations: TestingFileOperation[];
|
|
21
|
+
projectId: number;
|
|
22
|
+
};
|
|
23
|
+
export type TestingDownloadOptions = TestingConnectionOptions & {
|
|
24
|
+
branch?: string;
|
|
25
|
+
files: string[];
|
|
26
|
+
jobId: number;
|
|
27
|
+
outputDir?: string;
|
|
28
|
+
projectId: number;
|
|
29
|
+
};
|
|
30
|
+
export declare function listTestingProjects(options?: TestingConnectionOptions & {
|
|
31
|
+
name?: string;
|
|
32
|
+
}): Promise<TestingProject[]>;
|
|
33
|
+
export declare function createTestingProject(options: TestingConnectionOptions & {
|
|
34
|
+
description?: string;
|
|
35
|
+
name: string;
|
|
36
|
+
}): Promise<TestingProject>;
|
|
37
|
+
export declare function listTestingRobots(options: TestingConnectionOptions & {
|
|
38
|
+
projectId: number;
|
|
39
|
+
}): Promise<TestingRobot[]>;
|
|
40
|
+
export declare function createTestingRobot(options: TestingConnectionOptions & {
|
|
41
|
+
description?: string;
|
|
42
|
+
fwVersion?: string;
|
|
43
|
+
kind?: 'cloud' | 'QConnect';
|
|
44
|
+
name: string;
|
|
45
|
+
os?: string;
|
|
46
|
+
projectId: number;
|
|
47
|
+
robotType?: string;
|
|
48
|
+
}): Promise<TestingRobot>;
|
|
49
|
+
export declare function listTestingJobs(options: TestingConnectionOptions & {
|
|
50
|
+
name?: string;
|
|
51
|
+
projectId: number;
|
|
52
|
+
}): Promise<TestingJob[]>;
|
|
53
|
+
export declare function createTestingJob(options: TestingCreateJobOptions): Promise<TestingJobCreateResult>;
|
|
54
|
+
export declare function listTestingJobFiles(options: TestingConnectionOptions & {
|
|
55
|
+
jobId: number;
|
|
56
|
+
projectId: number;
|
|
57
|
+
}): Promise<TestingFileEntry[]>;
|
|
58
|
+
export declare function uploadTestingJobFiles(options: TestingUploadOptions): Promise<{
|
|
59
|
+
operations: TestingFileOperation[];
|
|
60
|
+
response: TestingRecord;
|
|
61
|
+
}>;
|
|
62
|
+
export declare function downloadTestingJobFiles(options: TestingDownloadOptions): Promise<TestingDownloadResult>;
|
|
63
|
+
export declare function runTestingBuild(options: TestingConnectionOptions & {
|
|
64
|
+
jobId: number;
|
|
65
|
+
projectId: number;
|
|
66
|
+
}): Promise<TestingBuildRunResult>;
|
|
67
|
+
export declare function listTestingBuilds(options: TestingConnectionOptions & {
|
|
68
|
+
jobId?: number;
|
|
69
|
+
projectId: number;
|
|
70
|
+
resultSize?: 'compact';
|
|
71
|
+
status?: TestingBuildStatus;
|
|
72
|
+
}): Promise<TestingBuild[]>;
|
|
73
|
+
export declare function getTestingBuildLogs(options: TestingConnectionOptions & {
|
|
74
|
+
buildId: number;
|
|
75
|
+
jobId: number;
|
|
76
|
+
output?: string;
|
|
77
|
+
projectId: number;
|
|
78
|
+
}): Promise<TestingLogResult>;
|
|
79
|
+
export declare function listTestingVariables(options: TestingConnectionOptions & {
|
|
80
|
+
jobId?: number;
|
|
81
|
+
projectId: number;
|
|
82
|
+
robotId?: number;
|
|
83
|
+
}): Promise<TestingVariable[]>;
|
|
84
|
+
export declare function createTestingVariable(options: TestingConnectionOptions & TestingCreateVariableRequest & {
|
|
85
|
+
projectId: number;
|
|
86
|
+
}): Promise<unknown>;
|
|
87
|
+
export declare function deleteTestingVariable(options: TestingConnectionOptions & {
|
|
88
|
+
projectId: number;
|
|
89
|
+
variableId: string;
|
|
90
|
+
}): Promise<{
|
|
91
|
+
projectId: number;
|
|
92
|
+
variableId: string;
|
|
93
|
+
}>;
|
|
94
|
+
export declare function buildFileAddOperations(input: TestingFileCollectionInput): TestingFileOperation[];
|
|
95
|
+
export declare function buildFileOperationsFromSpecs(specs: {
|
|
96
|
+
add?: string[];
|
|
97
|
+
remove?: string[];
|
|
98
|
+
replace?: string[];
|
|
99
|
+
}): TestingFileOperation[];
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
/* eslint-disable unicorn/text-encoding-identifier-case -- CRT file operation payloads require the literal utf-8 enum. */
|
|
2
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { dirname, join, relative, resolve, sep } from 'node:path';
|
|
4
|
+
import { resolveTestingCredentials } from './testing-config.js';
|
|
5
|
+
import { TestingGateway } from './testing-gateway.js';
|
|
6
|
+
import { createDefaultRobotSnapshotBase64, normalizeDownloadedFiles, normalizeTestingArray, normalizeTestingId, normalizeTestingRecord, redactTestingVariables, testingBuildLogsEndpoint, testingJobBuildsEndpoint, testingJobFilesDownloadEndpoint, testingJobFilesEndpoint, testingJobFilesUploadEndpoint, testingJobsEndpoint, testingProjectBuildsEndpoint, testingProjectsEndpoint, testingRobotJobsEndpoint, testingRobotsEndpoint, testingVariableEndpoint, testingVariablesEndpoint, } from './testing-types.js';
|
|
7
|
+
export const DEFAULT_TESTING_AUTHOR = {
|
|
8
|
+
email: 'agentia@copado.com',
|
|
9
|
+
name: 'Agentia CLI',
|
|
10
|
+
};
|
|
11
|
+
export async function listTestingProjects(options = {}) {
|
|
12
|
+
const credentials = resolveOptions(options);
|
|
13
|
+
const response = await TestingGateway.call({ endpoint: testingProjectsEndpoint(credentials.orgId), method: 'GET', service: 'platform' }, options);
|
|
14
|
+
const projects = normalizeTestingArray(response);
|
|
15
|
+
return filterByName(projects, options.name, false);
|
|
16
|
+
}
|
|
17
|
+
export async function createTestingProject(options) {
|
|
18
|
+
const credentials = resolveOptions(options);
|
|
19
|
+
const body = {
|
|
20
|
+
description: options.description ?? '',
|
|
21
|
+
name: options.name,
|
|
22
|
+
projectType: 'ta',
|
|
23
|
+
};
|
|
24
|
+
const response = await TestingGateway.call({ body, endpoint: testingProjectsEndpoint(credentials.orgId), method: 'POST', service: 'platform' }, options);
|
|
25
|
+
return normalizeTestingRecord(response);
|
|
26
|
+
}
|
|
27
|
+
export async function listTestingRobots(options) {
|
|
28
|
+
const response = await TestingGateway.call({ endpoint: testingRobotsEndpoint(options.projectId), method: 'GET', service: 'pace' }, options);
|
|
29
|
+
return normalizeTestingArray(response);
|
|
30
|
+
}
|
|
31
|
+
export async function createTestingRobot(options) {
|
|
32
|
+
const body = {
|
|
33
|
+
description: options.description ?? '',
|
|
34
|
+
fwVersion: options.fwVersion ?? 'stable',
|
|
35
|
+
name: options.name,
|
|
36
|
+
os: options.os ?? 'linux',
|
|
37
|
+
robotType: options.robotType ?? 'default',
|
|
38
|
+
runEnvironment: options.kind === 'QConnect' ? 'paceconnect' : 'cloud',
|
|
39
|
+
};
|
|
40
|
+
const response = await TestingGateway.call({ body, endpoint: testingRobotsEndpoint(options.projectId), method: 'POST', service: 'pace' }, options);
|
|
41
|
+
return normalizeTestingRecord(response);
|
|
42
|
+
}
|
|
43
|
+
export async function listTestingJobs(options) {
|
|
44
|
+
const response = await TestingGateway.call({ endpoint: testingJobsEndpoint(options.projectId), method: 'GET', service: 'pace' }, options);
|
|
45
|
+
const jobs = normalizeTestingArray(response);
|
|
46
|
+
return filterByName(jobs, options.name, true);
|
|
47
|
+
}
|
|
48
|
+
export async function createTestingJob(options) {
|
|
49
|
+
const body = {
|
|
50
|
+
description: options.description ?? '',
|
|
51
|
+
name: options.name,
|
|
52
|
+
storage: {
|
|
53
|
+
contentsBase64: createDefaultRobotSnapshotBase64(),
|
|
54
|
+
kind: 'snapshot',
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
const response = await TestingGateway.call({ body, endpoint: testingRobotJobsEndpoint(options.projectId, options.robotId), method: 'POST', service: 'pace' }, options);
|
|
58
|
+
const job = normalizeTestingRecord(response);
|
|
59
|
+
const jobId = normalizeTestingId(response);
|
|
60
|
+
const result = {
|
|
61
|
+
filesUploaded: 0,
|
|
62
|
+
job,
|
|
63
|
+
jobId,
|
|
64
|
+
projectId: options.projectId,
|
|
65
|
+
};
|
|
66
|
+
if (jobId !== undefined && options.fileInput !== undefined) {
|
|
67
|
+
const operations = buildFileAddOperations(options.fileInput);
|
|
68
|
+
if (operations.length > 0) {
|
|
69
|
+
const upload = await uploadTestingJobFiles({
|
|
70
|
+
...options,
|
|
71
|
+
jobId: numericId(jobId, 'job'),
|
|
72
|
+
operations: [{ op: 'remove', path: 'tests.robot' }, ...operations],
|
|
73
|
+
projectId: options.projectId,
|
|
74
|
+
});
|
|
75
|
+
result.filesUploaded = upload.operations.length - 1;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return result;
|
|
79
|
+
}
|
|
80
|
+
export async function listTestingJobFiles(options) {
|
|
81
|
+
const response = await TestingGateway.call({ endpoint: testingJobFilesEndpoint(options.projectId, options.jobId), method: 'GET', service: 'pace' }, options);
|
|
82
|
+
return normalizeDownloadedFiles(response);
|
|
83
|
+
}
|
|
84
|
+
export async function uploadTestingJobFiles(options) {
|
|
85
|
+
const body = {
|
|
86
|
+
author: options.author ?? DEFAULT_TESTING_AUTHOR,
|
|
87
|
+
operations: options.operations,
|
|
88
|
+
};
|
|
89
|
+
const response = await TestingGateway.call({
|
|
90
|
+
body,
|
|
91
|
+
endpoint: testingJobFilesUploadEndpoint(options.projectId, options.jobId, options.branch),
|
|
92
|
+
method: 'POST',
|
|
93
|
+
service: 'pace',
|
|
94
|
+
}, options);
|
|
95
|
+
return { operations: options.operations, response: normalizeTestingRecord(response) };
|
|
96
|
+
}
|
|
97
|
+
export async function downloadTestingJobFiles(options) {
|
|
98
|
+
const response = await TestingGateway.call({
|
|
99
|
+
body: { files: options.files },
|
|
100
|
+
endpoint: testingJobFilesDownloadEndpoint(options.projectId, options.jobId, options.branch),
|
|
101
|
+
method: 'POST',
|
|
102
|
+
service: 'pace',
|
|
103
|
+
}, options);
|
|
104
|
+
const files = normalizeDownloadedFiles(response);
|
|
105
|
+
if (options.outputDir !== undefined) {
|
|
106
|
+
writeDownloadedFiles(options.outputDir, files);
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
files,
|
|
110
|
+
jobId: options.jobId,
|
|
111
|
+
outputDir: options.outputDir,
|
|
112
|
+
projectId: options.projectId,
|
|
113
|
+
response,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
export async function runTestingBuild(options) {
|
|
117
|
+
const build = await TestingGateway.call({ body: {}, endpoint: testingJobBuildsEndpoint(options.projectId, options.jobId), method: 'POST', service: 'pace' }, options);
|
|
118
|
+
return { build, jobId: options.jobId, projectId: options.projectId };
|
|
119
|
+
}
|
|
120
|
+
export async function listTestingBuilds(options) {
|
|
121
|
+
const endpoint = options.jobId === undefined
|
|
122
|
+
? testingProjectBuildsEndpoint(options.projectId, { resultSize: options.resultSize, status: options.status })
|
|
123
|
+
: testingJobBuildsEndpoint(options.projectId, options.jobId, { resultSize: options.resultSize, status: options.status });
|
|
124
|
+
const response = await TestingGateway.call({ endpoint, method: 'GET', service: 'pace' }, options);
|
|
125
|
+
return normalizeTestingArray(response);
|
|
126
|
+
}
|
|
127
|
+
export async function getTestingBuildLogs(options) {
|
|
128
|
+
const log = await TestingGateway.call({
|
|
129
|
+
endpoint: testingBuildLogsEndpoint(options.projectId, options.jobId, options.buildId),
|
|
130
|
+
method: 'GET',
|
|
131
|
+
responseType: 'text',
|
|
132
|
+
service: 'pace',
|
|
133
|
+
}, options);
|
|
134
|
+
if (options.output !== undefined) {
|
|
135
|
+
mkdirSync(dirname(resolve(options.output)), { recursive: true });
|
|
136
|
+
writeFileSync(options.output, log, 'utf8');
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
buildId: options.buildId,
|
|
140
|
+
jobId: options.jobId,
|
|
141
|
+
log,
|
|
142
|
+
output: options.output,
|
|
143
|
+
projectId: options.projectId,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
export async function listTestingVariables(options) {
|
|
147
|
+
const response = await TestingGateway.call({ endpoint: testingVariablesEndpoint(options.projectId), method: 'GET', service: 'pace' }, options);
|
|
148
|
+
const variables = normalizeTestingArray(response)
|
|
149
|
+
.filter((variable) => options.jobId === undefined || variable.jobId?.toString() === options.jobId.toString())
|
|
150
|
+
.filter((variable) => options.robotId === undefined || variable.robotId?.toString() === options.robotId.toString());
|
|
151
|
+
return redactTestingVariables(variables);
|
|
152
|
+
}
|
|
153
|
+
export async function createTestingVariable(options) {
|
|
154
|
+
const body = [omitUndefined({
|
|
155
|
+
description: options.description,
|
|
156
|
+
jobId: options.jobId,
|
|
157
|
+
key: options.key,
|
|
158
|
+
robotId: options.robotId,
|
|
159
|
+
sensitive: options.sensitive,
|
|
160
|
+
type: options.type,
|
|
161
|
+
userSpecific: options.userSpecific,
|
|
162
|
+
value: options.value,
|
|
163
|
+
})];
|
|
164
|
+
const response = await TestingGateway.call({ body, endpoint: testingVariablesEndpoint(options.projectId), method: 'POST', service: 'pace' }, options);
|
|
165
|
+
return redactTestingVariables(response);
|
|
166
|
+
}
|
|
167
|
+
export async function deleteTestingVariable(options) {
|
|
168
|
+
await TestingGateway.call({ endpoint: testingVariableEndpoint(options.projectId, options.variableId), method: 'DELETE', service: 'pace' }, options);
|
|
169
|
+
return { projectId: options.projectId, variableId: options.variableId };
|
|
170
|
+
}
|
|
171
|
+
export function buildFileAddOperations(input) {
|
|
172
|
+
const operations = [];
|
|
173
|
+
for (const file of input.files ?? []) {
|
|
174
|
+
operations.push(fileOperationFromLocalFile(file, basenameForPath(file), 'add'));
|
|
175
|
+
}
|
|
176
|
+
for (const dir of input.dirs ?? []) {
|
|
177
|
+
const root = resolve(dir);
|
|
178
|
+
for (const file of collectFiles(root)) {
|
|
179
|
+
operations.push(fileOperationFromLocalFile(file, relative(root, file).split(sep).join('/'), 'add'));
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return operations;
|
|
183
|
+
}
|
|
184
|
+
export function buildFileOperationsFromSpecs(specs) {
|
|
185
|
+
return [
|
|
186
|
+
...(specs.add ?? []).map((spec) => fileOperationFromSpec(spec, 'add')),
|
|
187
|
+
...(specs.replace ?? []).map((spec) => fileOperationFromSpec(spec, 'replace')),
|
|
188
|
+
...(specs.remove ?? []).map((path) => ({ op: 'remove', path: normalizeRemotePath(path) })),
|
|
189
|
+
];
|
|
190
|
+
}
|
|
191
|
+
function fileOperationFromSpec(spec, op) {
|
|
192
|
+
const separator = spec.indexOf(':');
|
|
193
|
+
const localPath = separator === -1 ? spec : spec.slice(0, separator);
|
|
194
|
+
const remotePath = separator === -1 ? basenameForPath(localPath) : spec.slice(separator + 1);
|
|
195
|
+
return fileOperationFromLocalFile(localPath, remotePath, op);
|
|
196
|
+
}
|
|
197
|
+
function fileOperationFromLocalFile(localPath, remotePath, op) {
|
|
198
|
+
const path = normalizeRemotePath(remotePath);
|
|
199
|
+
return {
|
|
200
|
+
encoding: 'utf-8',
|
|
201
|
+
op,
|
|
202
|
+
path,
|
|
203
|
+
value: readFileSync(localPath, 'utf8'),
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
function collectFiles(root) {
|
|
207
|
+
if (!existsSync(root))
|
|
208
|
+
throw new Error(`Directory does not exist: ${root}`);
|
|
209
|
+
const entries = readdirSync(root).sort();
|
|
210
|
+
const files = [];
|
|
211
|
+
for (const entry of entries) {
|
|
212
|
+
const path = join(root, entry);
|
|
213
|
+
const stat = statSync(path);
|
|
214
|
+
if (stat.isDirectory())
|
|
215
|
+
files.push(...collectFiles(path));
|
|
216
|
+
else if (stat.isFile())
|
|
217
|
+
files.push(path);
|
|
218
|
+
}
|
|
219
|
+
return files;
|
|
220
|
+
}
|
|
221
|
+
function normalizeRemotePath(path) {
|
|
222
|
+
const normalized = path.trim().replaceAll('\\', '/').replace(/^\/+/u, '');
|
|
223
|
+
if (normalized.length === 0 || normalized.includes('..')) {
|
|
224
|
+
throw new Error(`Invalid CRT file path: ${path}`);
|
|
225
|
+
}
|
|
226
|
+
return normalized;
|
|
227
|
+
}
|
|
228
|
+
function writeDownloadedFiles(outputDir, files) {
|
|
229
|
+
const root = resolve(outputDir);
|
|
230
|
+
mkdirSync(root, { recursive: true });
|
|
231
|
+
for (const file of files) {
|
|
232
|
+
const remotePath = normalizeRemotePath(file.path ?? '');
|
|
233
|
+
const target = resolve(root, remotePath);
|
|
234
|
+
if (!target.startsWith(`${root}${sep}`) && target !== root) {
|
|
235
|
+
throw new Error(`Refusing to write CRT file outside output directory: ${remotePath}`);
|
|
236
|
+
}
|
|
237
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
238
|
+
const value = file.value ?? '';
|
|
239
|
+
const contents = file.encoding === 'base64' ? Buffer.from(value, 'base64') : Buffer.from(value, 'utf8');
|
|
240
|
+
writeFileSync(target, contents);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
function basenameForPath(path) {
|
|
244
|
+
const segments = path.replaceAll('\\', '/').split('/').filter(Boolean);
|
|
245
|
+
return segments.at(-1) ?? path;
|
|
246
|
+
}
|
|
247
|
+
function filterByName(records, name, insensitive) {
|
|
248
|
+
if (name === undefined || name.trim().length === 0)
|
|
249
|
+
return records;
|
|
250
|
+
const expected = insensitive ? name.toLowerCase() : name;
|
|
251
|
+
return records.filter((record) => {
|
|
252
|
+
const actual = record.name ?? '';
|
|
253
|
+
return insensitive ? actual.toLowerCase() === expected : actual === expected;
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
function numericId(value, label) {
|
|
257
|
+
const parsed = typeof value === 'number' ? value : Number.parseInt(value, 10);
|
|
258
|
+
if (!Number.isSafeInteger(parsed))
|
|
259
|
+
throw new Error(`CRT ${label} ID must be numeric: ${value}`);
|
|
260
|
+
return parsed;
|
|
261
|
+
}
|
|
262
|
+
function omitUndefined(record) {
|
|
263
|
+
return Object.fromEntries(Object.entries(record).filter(([, value]) => value !== undefined));
|
|
264
|
+
}
|
|
265
|
+
function resolveOptions(options) {
|
|
266
|
+
return { orgId: resolveTestingCredentials(options).orgId };
|
|
267
|
+
}
|
|
268
|
+
//# sourceMappingURL=testing-operations.js.map
|