@copado/agentia-cli 0.112.0-alpha.0 → 0.114.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 +2014 -450
- package/dist/commands/ai/agent/ask.d.ts +3 -0
- package/dist/commands/ai/agent/ask.js +14 -0
- package/dist/commands/ai/agent/ask.js.map +1 -1
- package/dist/commands/ai/agent/chat.d.ts +3 -0
- package/dist/commands/ai/agent/chat.js +14 -0
- package/dist/commands/ai/agent/chat.js.map +1 -1
- package/dist/commands/cicd/work/set.js +22 -14
- package/dist/commands/cicd/work/set.js.map +1 -1
- package/dist/commands/testing/build/abort.d.ts +7 -1
- package/dist/commands/testing/build/abort.js +13 -3
- package/dist/commands/testing/build/abort.js.map +1 -1
- package/dist/commands/testing/build/get.d.ts +18 -0
- package/dist/commands/testing/build/get.js +38 -0
- package/dist/commands/testing/build/get.js.map +1 -0
- package/dist/commands/testing/build/latest.d.ts +12 -0
- package/dist/commands/testing/build/latest.js +40 -0
- package/dist/commands/testing/build/latest.js.map +1 -0
- package/dist/commands/testing/build/list.d.ts +6 -1
- package/dist/commands/testing/build/list.js +23 -1
- package/dist/commands/testing/build/list.js.map +1 -1
- package/dist/commands/testing/build/logs.d.ts +6 -0
- package/dist/commands/testing/build/logs.js +8 -1
- package/dist/commands/testing/build/logs.js.map +1 -1
- package/dist/commands/testing/build/run.d.ts +27 -4
- package/dist/commands/testing/build/run.js +4 -18
- package/dist/commands/testing/build/run.js.map +1 -1
- package/dist/commands/testing/build/search.d.ts +21 -0
- package/dist/commands/testing/build/search.js +57 -0
- package/dist/commands/testing/build/search.js.map +1 -0
- package/dist/commands/testing/create.d.ts +1 -15
- package/dist/commands/testing/create.js +1 -43
- package/dist/commands/testing/create.js.map +1 -1
- package/dist/commands/testing/data-table/create.d.ts +30 -0
- package/dist/commands/testing/data-table/create.js +52 -0
- package/dist/commands/testing/data-table/create.js.map +1 -0
- package/dist/commands/testing/data-table/delete.d.ts +25 -0
- package/dist/commands/testing/data-table/delete.js +40 -0
- package/dist/commands/testing/data-table/delete.js.map +1 -0
- package/dist/commands/testing/data-table/export.d.ts +33 -0
- package/dist/commands/testing/data-table/export.js +59 -0
- package/dist/commands/testing/data-table/export.js.map +1 -0
- package/dist/commands/testing/data-table/get.d.ts +21 -0
- package/dist/commands/testing/data-table/get.js +35 -0
- package/dist/commands/testing/data-table/get.js.map +1 -0
- package/dist/commands/testing/data-table/list.d.ts +16 -0
- package/dist/commands/testing/data-table/list.js +33 -0
- package/dist/commands/testing/data-table/list.js.map +1 -0
- package/dist/commands/testing/data-table/update.d.ts +34 -0
- package/dist/commands/testing/data-table/update.js +64 -0
- package/dist/commands/testing/data-table/update.js.map +1 -0
- package/dist/commands/testing/job/create.d.ts +18 -0
- package/dist/commands/testing/job/create.js +60 -4
- package/dist/commands/testing/job/create.js.map +1 -1
- package/dist/commands/testing/job/delete.d.ts +6 -1
- package/dist/commands/testing/job/delete.js +13 -3
- package/dist/commands/testing/job/delete.js.map +1 -1
- package/dist/commands/testing/job/refs.d.ts +17 -0
- package/dist/commands/testing/job/refs.js +30 -0
- package/dist/commands/testing/job/refs.js.map +1 -0
- package/dist/commands/testing/job/schedule/get.d.ts +23 -0
- package/dist/commands/testing/job/schedule/get.js +31 -0
- package/dist/commands/testing/job/schedule/get.js.map +1 -0
- package/dist/commands/testing/job/schedule/remove.d.ts +24 -0
- package/dist/commands/testing/job/schedule/remove.js +34 -0
- package/dist/commands/testing/job/schedule/remove.js.map +1 -0
- package/dist/commands/testing/job/schedule/set.d.ts +23 -0
- package/dist/commands/testing/job/schedule/set.js +39 -0
- package/dist/commands/testing/job/schedule/set.js.map +1 -0
- package/dist/commands/testing/job/update.d.ts +2 -0
- package/dist/commands/testing/job/update.js +7 -1
- package/dist/commands/testing/job/update.js.map +1 -1
- package/dist/commands/testing/job/upload.d.ts +6 -0
- package/dist/commands/testing/job/upload.js +22 -0
- package/dist/commands/testing/job/upload.js.map +1 -1
- package/dist/commands/testing/live/continue.d.ts +20 -0
- package/dist/commands/testing/live/continue.js +21 -0
- package/dist/commands/testing/live/continue.js.map +1 -0
- package/dist/commands/testing/live/execute.d.ts +30 -0
- package/dist/commands/testing/live/execute.js +43 -0
- package/dist/commands/testing/live/execute.js.map +1 -0
- package/dist/commands/testing/live/history.d.ts +23 -0
- package/dist/commands/testing/live/history.js +35 -0
- package/dist/commands/testing/live/history.js.map +1 -0
- package/dist/commands/testing/live/inspect.d.ts +18 -0
- package/dist/commands/testing/live/inspect.js +30 -0
- package/dist/commands/testing/live/inspect.js.map +1 -0
- package/dist/commands/testing/live/interrupt.d.ts +20 -0
- package/dist/commands/testing/live/interrupt.js +21 -0
- package/dist/commands/testing/live/interrupt.js.map +1 -0
- package/dist/commands/testing/live/recorder.d.ts +23 -0
- package/dist/commands/testing/live/recorder.js +134 -0
- package/dist/commands/testing/live/recorder.js.map +1 -0
- package/dist/commands/testing/live/reports.d.ts +20 -0
- package/dist/commands/testing/live/reports.js +31 -0
- package/dist/commands/testing/live/reports.js.map +1 -0
- package/dist/commands/testing/live/screenshot.d.ts +23 -0
- package/dist/commands/testing/live/screenshot.js +25 -0
- package/dist/commands/testing/live/screenshot.js.map +1 -0
- package/dist/commands/testing/live/set-options.d.ts +27 -0
- package/dist/commands/testing/live/set-options.js +28 -0
- package/dist/commands/testing/live/set-options.js.map +1 -0
- package/dist/commands/testing/live/start.d.ts +29 -0
- package/dist/commands/testing/live/start.js +53 -0
- package/dist/commands/testing/live/start.js.map +1 -0
- package/dist/commands/testing/live/status.d.ts +19 -0
- package/dist/commands/testing/live/status.js +27 -0
- package/dist/commands/testing/live/status.js.map +1 -0
- package/dist/commands/testing/live/stop.d.ts +24 -0
- package/dist/commands/testing/live/stop.js +38 -0
- package/dist/commands/testing/live/stop.js.map +1 -0
- package/dist/commands/testing/live/view.d.ts +16 -0
- package/dist/commands/testing/live/view.js +36 -0
- package/dist/commands/testing/live/view.js.map +1 -0
- package/dist/commands/testing/logs.d.ts +6 -0
- package/dist/commands/testing/logs.js +8 -1
- package/dist/commands/testing/logs.js.map +1 -1
- package/dist/commands/testing/project/create.d.ts +3 -0
- package/dist/commands/testing/project/create.js +17 -1
- package/dist/commands/testing/project/create.js.map +1 -1
- package/dist/commands/testing/project/delete.d.ts +7 -2
- package/dist/commands/testing/project/delete.js +15 -3
- package/dist/commands/testing/project/delete.js.map +1 -1
- package/dist/commands/testing/project/get.js +1 -1
- package/dist/commands/testing/project/get.js.map +1 -1
- package/dist/commands/testing/project/permission/get.d.ts +13 -0
- package/dist/commands/testing/project/permission/get.js +21 -0
- package/dist/commands/testing/project/permission/get.js.map +1 -0
- package/dist/commands/testing/project/permission/list.d.ts +13 -0
- package/dist/commands/testing/project/permission/list.js +21 -0
- package/dist/commands/testing/project/permission/list.js.map +1 -0
- package/dist/commands/testing/project/permission/set.d.ts +19 -0
- package/dist/commands/testing/project/permission/set.js +33 -0
- package/dist/commands/testing/project/permission/set.js.map +1 -0
- package/dist/commands/testing/project/permission/update.d.ts +19 -0
- package/dist/commands/testing/project/permission/update.js +33 -0
- package/dist/commands/testing/project/permission/update.js.map +1 -0
- package/dist/commands/testing/project/update.d.ts +14 -0
- package/dist/commands/testing/project/update.js +28 -0
- package/dist/commands/testing/project/update.js.map +1 -0
- package/dist/commands/testing/robot/create.d.ts +4 -1
- package/dist/commands/testing/robot/create.js +20 -5
- package/dist/commands/testing/robot/create.js.map +1 -1
- package/dist/commands/testing/robot/delete.d.ts +6 -1
- package/dist/commands/testing/robot/delete.js +13 -3
- package/dist/commands/testing/robot/delete.js.map +1 -1
- package/dist/commands/testing/robot/update.d.ts +19 -0
- package/dist/commands/testing/robot/update.js +49 -0
- package/dist/commands/testing/robot/update.js.map +1 -0
- package/dist/commands/testing/run.d.ts +27 -4
- package/dist/commands/testing/run.js +4 -11
- package/dist/commands/testing/run.js.map +1 -1
- package/dist/commands/testing/test/run.d.ts +27 -11
- package/dist/commands/testing/test/run.js +5 -37
- package/dist/commands/testing/test/run.js.map +1 -1
- package/dist/commands/testing/update.d.ts +6 -0
- package/dist/commands/testing/update.js +10 -0
- package/dist/commands/testing/update.js.map +1 -1
- package/dist/commands/testing/user-parameter/create.d.ts +19 -0
- package/dist/commands/testing/user-parameter/create.js +45 -0
- package/dist/commands/testing/user-parameter/create.js.map +1 -0
- package/dist/commands/testing/user-parameter/delete.d.ts +21 -0
- package/dist/commands/testing/user-parameter/delete.js +33 -0
- package/dist/commands/testing/user-parameter/delete.js.map +1 -0
- package/dist/commands/testing/user-parameter/get.d.ts +13 -0
- package/dist/commands/testing/user-parameter/get.js +21 -0
- package/dist/commands/testing/user-parameter/get.js.map +1 -0
- package/dist/commands/testing/user-parameter/list.d.ts +12 -0
- package/dist/commands/testing/user-parameter/list.js +23 -0
- package/dist/commands/testing/user-parameter/list.js.map +1 -0
- package/dist/commands/testing/user-parameter/update.d.ts +23 -0
- package/dist/commands/testing/user-parameter/update.js +45 -0
- package/dist/commands/testing/user-parameter/update.js.map +1 -0
- package/dist/commands/testing/variable/create.d.ts +6 -2
- package/dist/commands/testing/variable/create.js +14 -5
- package/dist/commands/testing/variable/create.js.map +1 -1
- package/dist/commands/testing/variable/delete.d.ts +6 -1
- package/dist/commands/testing/variable/delete.js +13 -3
- package/dist/commands/testing/variable/delete.js.map +1 -1
- package/dist/commands/testing/variable/get.d.ts +13 -0
- package/dist/commands/testing/variable/get.js +25 -0
- package/dist/commands/testing/variable/get.js.map +1 -0
- package/dist/commands/testing/variable/list.d.ts +4 -0
- package/dist/commands/testing/variable/list.js +15 -3
- package/dist/commands/testing/variable/list.js.map +1 -1
- package/dist/commands/testing/variable/update.d.ts +6 -2
- package/dist/commands/testing/variable/update.js +11 -5
- package/dist/commands/testing/variable/update.js.map +1 -1
- package/dist/lib/ai-chat-session.d.ts +2 -1
- package/dist/lib/ai-chat-session.js +8 -0
- package/dist/lib/ai-chat-session.js.map +1 -1
- package/dist/lib/ai-gateway.d.ts +5 -2
- package/dist/lib/ai-gateway.js +44 -5
- package/dist/lib/ai-gateway.js.map +1 -1
- package/dist/lib/ai-operations.d.ts +4 -2
- package/dist/lib/ai-operations.js +29 -3
- package/dist/lib/ai-operations.js.map +1 -1
- package/dist/lib/ai-types.d.ts +29 -0
- package/dist/lib/ai-types.js +25 -0
- package/dist/lib/ai-types.js.map +1 -1
- package/dist/lib/cicd-gateway.d.ts +6 -0
- package/dist/lib/cicd-gateway.js +13 -2
- package/dist/lib/cicd-gateway.js.map +1 -1
- package/dist/lib/credential-store.d.ts +3 -0
- package/dist/lib/credential-store.js +16 -0
- package/dist/lib/credential-store.js.map +1 -1
- package/dist/lib/credential-sync.d.ts +35 -0
- package/dist/lib/credential-sync.js +249 -0
- package/dist/lib/credential-sync.js.map +1 -0
- package/dist/lib/debug.js +3 -1
- package/dist/lib/debug.js.map +1 -1
- package/dist/lib/defaults-store.d.ts +18 -0
- package/dist/lib/defaults-store.js +182 -89
- package/dist/lib/defaults-store.js.map +1 -1
- package/dist/lib/environment-types.js +1 -1
- package/dist/lib/mcp/ai-tools.d.ts +3 -0
- package/dist/lib/mcp/ai-tools.js +6 -0
- package/dist/lib/mcp/ai-tools.js.map +1 -1
- package/dist/lib/mcp/command-id.js +3 -0
- package/dist/lib/mcp/command-id.js.map +1 -1
- package/dist/lib/mcp/redaction.d.ts +1 -2
- package/dist/lib/mcp/redaction.js +1 -23
- package/dist/lib/mcp/redaction.js.map +1 -1
- package/dist/lib/mcp/testing-live-tools.d.ts +126 -0
- package/dist/lib/mcp/testing-live-tools.js +215 -0
- package/dist/lib/mcp/testing-live-tools.js.map +1 -0
- package/dist/lib/mcp/testing-tools.d.ts +455 -9
- package/dist/lib/mcp/testing-tools.js +677 -37
- package/dist/lib/mcp/testing-tools.js.map +1 -1
- package/dist/lib/mcp/tools.d.ts +554 -14
- package/dist/lib/mcp/work-tools.js +7 -4
- package/dist/lib/mcp/work-tools.js.map +1 -1
- package/dist/lib/secret-redaction.d.ts +2 -0
- package/dist/lib/secret-redaction.js +24 -0
- package/dist/lib/secret-redaction.js.map +1 -0
- package/dist/lib/testing-config.d.ts +3 -0
- package/dist/lib/testing-config.js +9 -0
- package/dist/lib/testing-config.js.map +1 -1
- package/dist/lib/testing-confirmation.d.ts +14 -0
- package/dist/lib/testing-confirmation.js +17 -0
- package/dist/lib/testing-confirmation.js.map +1 -0
- package/dist/lib/testing-data-command.d.ts +21 -0
- package/dist/lib/testing-data-command.js +49 -0
- package/dist/lib/testing-data-command.js.map +1 -0
- package/dist/lib/testing-data-core.d.ts +22 -0
- package/dist/lib/testing-data-core.js +208 -0
- package/dist/lib/testing-data-core.js.map +1 -0
- package/dist/lib/testing-data-export.d.ts +1 -0
- package/dist/lib/testing-data-export.js +2 -0
- package/dist/lib/testing-data-export.js.map +1 -0
- package/dist/lib/testing-data-import.d.ts +1 -0
- package/dist/lib/testing-data-import.js +2 -0
- package/dist/lib/testing-data-import.js.map +1 -0
- package/dist/lib/testing-data-io.d.ts +21 -0
- package/dist/lib/testing-data-io.js +236 -0
- package/dist/lib/testing-data-io.js.map +1 -0
- package/dist/lib/testing-data-operations.d.ts +36 -0
- package/dist/lib/testing-data-operations.js +91 -0
- package/dist/lib/testing-data-operations.js.map +1 -0
- package/dist/lib/testing-data-types.d.ts +41 -0
- package/dist/lib/testing-data-types.js +3 -0
- package/dist/lib/testing-data-types.js.map +1 -0
- package/dist/lib/testing-flags.d.ts +62 -1
- package/dist/lib/testing-flags.js +177 -1
- package/dist/lib/testing-flags.js.map +1 -1
- package/dist/lib/testing-gateway.d.ts +17 -3
- package/dist/lib/testing-gateway.js +175 -45
- package/dist/lib/testing-gateway.js.map +1 -1
- package/dist/lib/testing-live-command.d.ts +24 -0
- package/dist/lib/testing-live-command.js +56 -0
- package/dist/lib/testing-live-command.js.map +1 -0
- package/dist/lib/testing-live-endpoints.d.ts +14 -0
- package/dist/lib/testing-live-endpoints.js +34 -0
- package/dist/lib/testing-live-endpoints.js.map +1 -0
- package/dist/lib/testing-live-history.d.ts +11 -0
- package/dist/lib/testing-live-history.js +96 -0
- package/dist/lib/testing-live-history.js.map +1 -0
- package/dist/lib/testing-live-operations.d.ts +118 -0
- package/dist/lib/testing-live-operations.js +676 -0
- package/dist/lib/testing-live-operations.js.map +1 -0
- package/dist/lib/testing-live-recorder.d.ts +14 -0
- package/dist/lib/testing-live-recorder.js +122 -0
- package/dist/lib/testing-live-recorder.js.map +1 -0
- package/dist/lib/testing-live-robot.d.ts +16 -0
- package/dist/lib/testing-live-robot.js +158 -0
- package/dist/lib/testing-live-robot.js.map +1 -0
- package/dist/lib/testing-live-state.d.ts +4 -0
- package/dist/lib/testing-live-state.js +90 -0
- package/dist/lib/testing-live-state.js.map +1 -0
- package/dist/lib/testing-live-types.d.ts +92 -0
- package/dist/lib/testing-live-types.js +2 -0
- package/dist/lib/testing-live-types.js.map +1 -0
- package/dist/lib/testing-operations.d.ts +157 -5
- package/dist/lib/testing-operations.js +829 -66
- package/dist/lib/testing-operations.js.map +1 -1
- package/dist/lib/testing-run-command.d.ts +25 -0
- package/dist/lib/testing-run-command.js +94 -0
- package/dist/lib/testing-run-command.js.map +1 -0
- package/dist/lib/testing-sanitization.d.ts +11 -0
- package/dist/lib/testing-sanitization.js +135 -0
- package/dist/lib/testing-sanitization.js.map +1 -0
- package/dist/lib/testing-schedule.d.ts +23 -0
- package/dist/lib/testing-schedule.js +48 -0
- package/dist/lib/testing-schedule.js.map +1 -0
- package/dist/lib/testing-secret-input.d.ts +12 -0
- package/dist/lib/testing-secret-input.js +56 -0
- package/dist/lib/testing-secret-input.js.map +1 -0
- package/dist/lib/testing-session.d.ts +8 -0
- package/dist/lib/testing-session.js +82 -0
- package/dist/lib/testing-session.js.map +1 -0
- package/dist/lib/testing-types.d.ts +143 -13
- package/dist/lib/testing-types.js +110 -5
- package/dist/lib/testing-types.js.map +1 -1
- package/dist/lib/testing-watch.d.ts +4 -0
- package/dist/lib/testing-watch.js +45 -0
- package/dist/lib/testing-watch.js.map +1 -0
- package/dist/lib/work-types.d.ts +1 -0
- package/dist/lib/work-types.js.map +1 -1
- package/oclif.manifest.json +7523 -2891
- package/package.json +12 -1
- package/templates/skills/agentia-testing/SKILL.md +9 -6
- package/templates/skills/agentia-testing/references/tests.md +70 -36
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Flags } from '@oclif/core';
|
|
2
|
-
import { TESTING_RECORD_BUILD_OPTIONS } from './testing-types.js';
|
|
2
|
+
import { TESTING_CONSOLE_COLORS, TESTING_RECORD_BUILD_OPTIONS, TESTING_RUN_TYPES, } from './testing-types.js';
|
|
3
3
|
export const testingProjectFlag = {
|
|
4
4
|
project: Flags.integer({
|
|
5
5
|
char: 'p',
|
|
@@ -7,6 +7,38 @@ export const testingProjectFlag = {
|
|
|
7
7
|
required: true,
|
|
8
8
|
}),
|
|
9
9
|
};
|
|
10
|
+
export const testingYesFlag = {
|
|
11
|
+
yes: Flags.boolean({
|
|
12
|
+
char: 'y',
|
|
13
|
+
default: false,
|
|
14
|
+
description: 'Skip the destructive confirmation prompt.',
|
|
15
|
+
}),
|
|
16
|
+
};
|
|
17
|
+
export const testingSecretInputFlags = {
|
|
18
|
+
'no-trim': Flags.boolean({
|
|
19
|
+
default: false,
|
|
20
|
+
description: 'Preserve leading and trailing whitespace in the supplied value.',
|
|
21
|
+
exclusive: ['trim'],
|
|
22
|
+
}),
|
|
23
|
+
trim: Flags.boolean({
|
|
24
|
+
default: false,
|
|
25
|
+
description: 'Trim leading and trailing whitespace (the default policy).',
|
|
26
|
+
exclusive: ['no-trim'],
|
|
27
|
+
}),
|
|
28
|
+
value: Flags.string({
|
|
29
|
+
description: 'Value supplied directly. Prefer file/stdin for sensitive values.',
|
|
30
|
+
exclusive: ['value-file', 'value-stdin'],
|
|
31
|
+
}),
|
|
32
|
+
'value-file': Flags.string({
|
|
33
|
+
description: 'Read the value from a bounded regular file.',
|
|
34
|
+
exclusive: ['value', 'value-stdin'],
|
|
35
|
+
}),
|
|
36
|
+
'value-stdin': Flags.boolean({
|
|
37
|
+
default: false,
|
|
38
|
+
description: 'Read the value from stdin.',
|
|
39
|
+
exclusive: ['value', 'value-file'],
|
|
40
|
+
}),
|
|
41
|
+
};
|
|
10
42
|
export const testingJobFlag = {
|
|
11
43
|
job: Flags.integer({
|
|
12
44
|
char: 'j',
|
|
@@ -23,14 +55,52 @@ export const testingRunOptionFlags = {
|
|
|
23
55
|
branch: Flags.string({
|
|
24
56
|
description: 'Git branch/ref to use for this run. Mutually exclusive with --tag.',
|
|
25
57
|
}),
|
|
58
|
+
'console-colors': Flags.option({
|
|
59
|
+
description: 'Control Robot Framework console colors.',
|
|
60
|
+
options: [...TESTING_CONSOLE_COLORS],
|
|
61
|
+
})(),
|
|
62
|
+
'console-width': Flags.integer({
|
|
63
|
+
description: 'Set Robot Framework console width.',
|
|
64
|
+
min: 1,
|
|
65
|
+
}),
|
|
66
|
+
datatable: Flags.string({
|
|
67
|
+
description: 'Data table and 1-based datasets as TABLE_ID:1,2-4. Repeat for zipped table selections.',
|
|
68
|
+
multiple: true,
|
|
69
|
+
}),
|
|
70
|
+
doc: Flags.string({
|
|
71
|
+
description: 'Set top-level Robot Framework suite documentation.',
|
|
72
|
+
}),
|
|
73
|
+
'dry-run': Flags.boolean({
|
|
74
|
+
default: false,
|
|
75
|
+
description: 'Validate Robot Framework data without executing library keywords.',
|
|
76
|
+
}),
|
|
26
77
|
exclude: Flags.string({
|
|
27
78
|
description: 'Robot Framework tag to exclude from this run. Can be provided multiple times.',
|
|
28
79
|
multiple: true,
|
|
29
80
|
}),
|
|
81
|
+
'exit-on-error': Flags.boolean({
|
|
82
|
+
default: false,
|
|
83
|
+
description: 'Stop execution when Robot Framework encounters a data or import error.',
|
|
84
|
+
}),
|
|
85
|
+
'exit-on-failure': Flags.boolean({
|
|
86
|
+
default: false,
|
|
87
|
+
description: 'Stop execution when a critical Robot Framework test fails.',
|
|
88
|
+
}),
|
|
30
89
|
include: Flags.string({
|
|
31
90
|
description: 'Robot Framework tag to include in this run. Can be provided multiple times.',
|
|
32
91
|
multiple: true,
|
|
33
92
|
}),
|
|
93
|
+
metadata: Flags.string({
|
|
94
|
+
description: 'Robot Framework suite metadata. Can be provided multiple times.',
|
|
95
|
+
multiple: true,
|
|
96
|
+
}),
|
|
97
|
+
name: Flags.string({
|
|
98
|
+
description: 'Set the top-level Robot Framework suite name.',
|
|
99
|
+
}),
|
|
100
|
+
'no-exit-code': Flags.boolean({
|
|
101
|
+
default: false,
|
|
102
|
+
description: 'Return exit code zero even when the waited CRT run fails.',
|
|
103
|
+
}),
|
|
34
104
|
param: Flags.string({
|
|
35
105
|
description: 'Execution parameter in KEY=VALUE format. Can be provided multiple times.',
|
|
36
106
|
multiple: true,
|
|
@@ -46,9 +116,63 @@ export const testingRunOptionFlags = {
|
|
|
46
116
|
default: false,
|
|
47
117
|
description: 'Rerun failed tests from the latest failed run. Mutually exclusive with --rerun-failed.',
|
|
48
118
|
}),
|
|
119
|
+
'run-empty-suite': Flags.boolean({
|
|
120
|
+
default: false,
|
|
121
|
+
description: 'Execute even when the selected Robot Framework suite is empty.',
|
|
122
|
+
}),
|
|
123
|
+
'run-type': Flags.option({
|
|
124
|
+
description: 'CRT run type. Regression is sent as normal; development is sent unchanged.',
|
|
125
|
+
options: [...TESTING_RUN_TYPES],
|
|
126
|
+
})(),
|
|
127
|
+
'save-artifacts': Flags.string({
|
|
128
|
+
description: 'Save the completed run output ZIP to this explicit file path.',
|
|
129
|
+
}),
|
|
130
|
+
'set-tag': Flags.string({
|
|
131
|
+
description: 'Set a tag on all executed tests. Can be provided multiple times.',
|
|
132
|
+
multiple: true,
|
|
133
|
+
}),
|
|
134
|
+
skip: Flags.string({
|
|
135
|
+
description: 'Skip tests matching this tag. Can be provided multiple times.',
|
|
136
|
+
multiple: true,
|
|
137
|
+
}),
|
|
138
|
+
'skip-on-failure': Flags.string({
|
|
139
|
+
description: 'Skip failures for tests matching this tag. Can be provided multiple times.',
|
|
140
|
+
multiple: true,
|
|
141
|
+
}),
|
|
142
|
+
'stream-logs': Flags.boolean({
|
|
143
|
+
default: false,
|
|
144
|
+
description: 'Stream new CRT log text while waiting (human output only).',
|
|
145
|
+
}),
|
|
146
|
+
suite: Flags.string({
|
|
147
|
+
description: 'Select a Robot Framework suite. Can be provided multiple times.',
|
|
148
|
+
multiple: true,
|
|
149
|
+
}),
|
|
49
150
|
tag: Flags.string({
|
|
50
151
|
description: 'Git tag to use for this run. Mutually exclusive with --branch.',
|
|
51
152
|
}),
|
|
153
|
+
test: Flags.string({
|
|
154
|
+
description: 'Select a Robot Framework test/task. Can be provided multiple times.',
|
|
155
|
+
multiple: true,
|
|
156
|
+
}),
|
|
157
|
+
timeout: Flags.integer({
|
|
158
|
+
default: 60,
|
|
159
|
+
description: 'Maximum number of minutes to wait for a terminal result.',
|
|
160
|
+
min: 1,
|
|
161
|
+
}),
|
|
162
|
+
variable: Flags.string({
|
|
163
|
+
description: 'Set a Robot Framework variable. Can be provided multiple times.',
|
|
164
|
+
multiple: true,
|
|
165
|
+
}),
|
|
166
|
+
'wait-for-result': Flags.boolean({
|
|
167
|
+
default: false,
|
|
168
|
+
description: 'Poll CRT until the started build reaches a terminal result.',
|
|
169
|
+
}),
|
|
170
|
+
watch: Flags.string({
|
|
171
|
+
description: 'Watch a directory and rerun after debounced file changes (human output only).',
|
|
172
|
+
}),
|
|
173
|
+
xunit: Flags.string({
|
|
174
|
+
description: 'Extract xunit_report.xml from completed run artifacts to this explicit file path.',
|
|
175
|
+
}),
|
|
52
176
|
};
|
|
53
177
|
export const testingOutputFlags = {
|
|
54
178
|
output: Flags.string({
|
|
@@ -56,6 +180,31 @@ export const testingOutputFlags = {
|
|
|
56
180
|
description: 'Write raw output to this file path.',
|
|
57
181
|
}),
|
|
58
182
|
};
|
|
183
|
+
export const testingLogOptionFlags = {
|
|
184
|
+
container: Flags.string({
|
|
185
|
+
description: 'Read a specific CRT log stream/container.',
|
|
186
|
+
}),
|
|
187
|
+
'limit-bytes': Flags.integer({
|
|
188
|
+
description: 'Limit fetched log data to this many bytes.',
|
|
189
|
+
min: 1,
|
|
190
|
+
}),
|
|
191
|
+
pretty: Flags.boolean({
|
|
192
|
+
default: false,
|
|
193
|
+
description: 'Request readable log formatting.',
|
|
194
|
+
}),
|
|
195
|
+
'since-seconds': Flags.integer({
|
|
196
|
+
description: 'Fetch log lines from the last N seconds.',
|
|
197
|
+
min: 0,
|
|
198
|
+
}),
|
|
199
|
+
'tail-lines': Flags.integer({
|
|
200
|
+
description: 'Return only the last N log lines.',
|
|
201
|
+
min: 0,
|
|
202
|
+
}),
|
|
203
|
+
timestamps: Flags.boolean({
|
|
204
|
+
default: false,
|
|
205
|
+
description: 'Add timestamps to log lines.',
|
|
206
|
+
}),
|
|
207
|
+
};
|
|
59
208
|
export const testingFileInputFlags = {
|
|
60
209
|
dir: Flags.string({
|
|
61
210
|
description: 'Directory containing Robot Framework files to upload. Can be provided multiple times.',
|
|
@@ -76,6 +225,16 @@ export const testingGitStorageFlags = {
|
|
|
76
225
|
}),
|
|
77
226
|
'git-password': Flags.string({
|
|
78
227
|
description: 'Git password/token for repository access. Prefer --git-domain-credential when available.',
|
|
228
|
+
exclusive: ['git-password-file', 'git-password-stdin'],
|
|
229
|
+
}),
|
|
230
|
+
'git-password-file': Flags.string({
|
|
231
|
+
description: 'Read a bounded Git password/token from a regular file.',
|
|
232
|
+
exclusive: ['git-password', 'git-password-stdin'],
|
|
233
|
+
}),
|
|
234
|
+
'git-password-stdin': Flags.boolean({
|
|
235
|
+
default: false,
|
|
236
|
+
description: 'Read a Git password/token from stdin.',
|
|
237
|
+
exclusive: ['git-password', 'git-password-file'],
|
|
79
238
|
}),
|
|
80
239
|
'git-tag': Flags.string({
|
|
81
240
|
description: 'Git tag to use for a Git-backed CRT job. Mutually exclusive with --git-branch.',
|
|
@@ -114,15 +273,32 @@ export function testingRunOptions(flags, ids) {
|
|
|
114
273
|
}
|
|
115
274
|
return {
|
|
116
275
|
branch: flags.branch,
|
|
276
|
+
consoleColors: flags['console-colors'],
|
|
277
|
+
consoleWidth: flags['console-width'],
|
|
278
|
+
dataTables: flags.datatable,
|
|
279
|
+
doc: flags.doc,
|
|
280
|
+
dryRun: flags['dry-run'],
|
|
117
281
|
exclude: flags.exclude,
|
|
282
|
+
exitOnError: flags['exit-on-error'],
|
|
283
|
+
exitOnFailure: flags['exit-on-failure'],
|
|
118
284
|
include: flags.include,
|
|
119
285
|
jobId: ids.jobId,
|
|
286
|
+
metadata: flags.metadata,
|
|
287
|
+
name: flags.name,
|
|
120
288
|
parameters: (flags.param ?? []).map((param) => parseTestingParameter(param)),
|
|
121
289
|
projectId: ids.projectId,
|
|
122
290
|
record: flags.record,
|
|
123
291
|
rerunFailedFromLatest: flags['rerun-failed-from-latest'] === true ? true : undefined,
|
|
124
292
|
rerunFailedId: flags['rerun-failed'],
|
|
293
|
+
runEmptySuite: flags['run-empty-suite'],
|
|
294
|
+
runType: flags['run-type'],
|
|
295
|
+
setTags: flags['set-tag'],
|
|
296
|
+
skip: flags.skip,
|
|
297
|
+
skipOnFailure: flags['skip-on-failure'],
|
|
298
|
+
suites: flags.suite,
|
|
125
299
|
tag: flags.tag,
|
|
300
|
+
tests: flags.test,
|
|
301
|
+
variables: flags.variable,
|
|
126
302
|
};
|
|
127
303
|
}
|
|
128
304
|
function parseTestingParameter(spec) {
|
|
@@ -1 +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;AAMjC,OAAO,
|
|
1
|
+
{"version":3,"file":"testing-flags.js","sourceRoot":"","sources":["../../src/lib/testing-flags.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
import { URL } from 'node:url';
|
|
2
2
|
import { type TestingConnectionOverrides } from './testing-config.js';
|
|
3
3
|
export declare const TESTING_DEFAULT_TIMEOUT_MS = 45000;
|
|
4
|
+
export declare const TESTING_MAX_RESPONSE_BYTES: number;
|
|
5
|
+
export declare const TESTING_MAX_ARTIFACT_BYTES: number;
|
|
4
6
|
export type TestingRequest = {
|
|
7
|
+
auth?: {
|
|
8
|
+
cookie: string;
|
|
9
|
+
mode: 'session';
|
|
10
|
+
xsrfToken?: string;
|
|
11
|
+
};
|
|
5
12
|
body?: unknown;
|
|
6
13
|
endpoint: string;
|
|
14
|
+
headers?: Record<string, string>;
|
|
15
|
+
maxResponseBytes?: number;
|
|
7
16
|
method: 'DELETE' | 'GET' | 'PATCH' | 'POST' | 'PUT';
|
|
8
|
-
responseType?: 'json' | 'text';
|
|
9
|
-
service: 'pace' | 'platform';
|
|
17
|
+
responseType?: 'buffer' | 'json' | 'text';
|
|
18
|
+
service: 'metrics' | 'pace' | 'platform' | 'session' | 'test-data';
|
|
19
|
+
sessionPath?: string;
|
|
10
20
|
};
|
|
11
|
-
export type TestingHttpRequestFn = (url: URL, method: string, headers: Record<string, string>, body: unknown, timeoutMs: number) => Promise<string>;
|
|
21
|
+
export type TestingHttpRequestFn = (url: URL, method: string, headers: Record<string, string>, body: unknown, timeoutMs: number, maxResponseBytes?: number) => Promise<Buffer | string>;
|
|
12
22
|
export type TestingGatewayCallOptions = TestingConnectionOverrides & {
|
|
13
23
|
timeoutMs?: number;
|
|
14
24
|
};
|
|
@@ -28,6 +38,7 @@ export declare class TestingGatewayError extends Error {
|
|
|
28
38
|
}
|
|
29
39
|
export declare class TestingGateway {
|
|
30
40
|
static call<T = unknown>(request: TestingRequest, options?: TestingGatewayCallOptions): Promise<T>;
|
|
41
|
+
static downloadArtifact(location: string, options?: TestingGatewayCallOptions): Promise<Buffer>;
|
|
31
42
|
static resolveAndValidateDomain(pat: string, orgId: number, customDomain?: string): Promise<string>;
|
|
32
43
|
static validateCredentials(options: {
|
|
33
44
|
domain: string;
|
|
@@ -35,6 +46,9 @@ export declare class TestingGateway {
|
|
|
35
46
|
pat: string;
|
|
36
47
|
}): Promise<void>;
|
|
37
48
|
}
|
|
49
|
+
export declare function resolveTestingArtifactUrl(location: string, configuredDomain: string): URL;
|
|
50
|
+
export declare function resolveTestingSessionUrl(location: string, configuredDomain: string): URL;
|
|
51
|
+
export declare function resolveTestingSessionWebSocketUrl(sessionPath: string, configuredDomain: string): URL;
|
|
38
52
|
export declare function validateCrtCredentials(options: {
|
|
39
53
|
domain: string;
|
|
40
54
|
orgId: number;
|
|
@@ -2,10 +2,13 @@ import * as http from 'node:http';
|
|
|
2
2
|
import * as https from 'node:https';
|
|
3
3
|
import { URL } from 'node:url';
|
|
4
4
|
import { createTransactionId, CRT_GATEWAY_EVENT_SERVICE, gatewayRefererHeaders, gatewayUserAgent, getGatewayTransactionId, scheduleGatewayEventLog, } from './cicd-gateway.js';
|
|
5
|
-
import {
|
|
6
|
-
import { CRT_DISCOVERY_DOMAINS, normalizeCrtDomain, paceBaseUrl, platformBaseUrl, resolveTestingCredentials, } from './testing-config.js';
|
|
5
|
+
import { logVerboseDebug } from './debug.js';
|
|
6
|
+
import { CRT_DISCOVERY_DOMAINS, metricsBaseUrl, normalizeCrtDomain, paceBaseUrl, platformBaseUrl, resolveTestingCredentials, sessionsBaseUrl, testDataBaseUrl, } from './testing-config.js';
|
|
7
|
+
import { sanitizeTestingUrl, summarizeTestingDebugRequest, summarizeTestingDebugResponse, TESTING_REDACTED, testingProviderErrorMessage, } from './testing-sanitization.js';
|
|
7
8
|
import { testingProjectsEndpoint } from './testing-types.js';
|
|
8
9
|
export const TESTING_DEFAULT_TIMEOUT_MS = 45_000;
|
|
10
|
+
export const TESTING_MAX_RESPONSE_BYTES = 10 * 1024 * 1024;
|
|
11
|
+
export const TESTING_MAX_ARTIFACT_BYTES = 100 * 1024 * 1024;
|
|
9
12
|
export class TestingGatewayError extends Error {
|
|
10
13
|
requestMethod;
|
|
11
14
|
requestUrl;
|
|
@@ -40,22 +43,9 @@ function parseRetryAfterHeader(value) {
|
|
|
40
43
|
const dateMs = Date.parse(trimmed);
|
|
41
44
|
return Number.isNaN(dateMs) ? undefined : Math.max(0, Math.ceil((dateMs - Date.now()) / 1000));
|
|
42
45
|
}
|
|
43
|
-
function
|
|
44
|
-
try {
|
|
45
|
-
const parsed = JSON.parse(raw);
|
|
46
|
-
for (const value of [parsed.message, parsed.error, parsed.title]) {
|
|
47
|
-
if (typeof value === 'string' && value.trim().length > 0)
|
|
48
|
-
return value.trim();
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
catch {
|
|
52
|
-
return raw.trim().length > 0 ? raw.trim() : undefined;
|
|
53
|
-
}
|
|
54
|
-
return undefined;
|
|
55
|
-
}
|
|
56
|
-
function defaultHttpsRequest(url, method, headers, body, timeoutMs) {
|
|
46
|
+
function defaultHttpsRequest(url, method, headers, body, timeoutMs, maxResponseBytes = TESTING_MAX_RESPONSE_BYTES) {
|
|
57
47
|
const hasBody = body !== undefined;
|
|
58
|
-
const payload = hasBody ? JSON.stringify(body) : '';
|
|
48
|
+
const payload = hasBody ? (typeof body === 'string' ? body : JSON.stringify(body)) : '';
|
|
59
49
|
const requestHeaders = {
|
|
60
50
|
Accept: 'application/json',
|
|
61
51
|
...headers,
|
|
@@ -75,17 +65,25 @@ function defaultHttpsRequest(url, method, headers, body, timeoutMs) {
|
|
|
75
65
|
timeout: timeoutMs,
|
|
76
66
|
}, (res) => {
|
|
77
67
|
const chunks = [];
|
|
78
|
-
|
|
68
|
+
let responseBytes = 0;
|
|
69
|
+
res.on('data', (chunk) => {
|
|
70
|
+
responseBytes += chunk.length;
|
|
71
|
+
if (responseBytes > maxResponseBytes) {
|
|
72
|
+
req.destroy(new TestingGatewayError(`CRT response exceeded the ${maxResponseBytes} byte safety limit.`));
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
chunks.push(chunk);
|
|
76
|
+
});
|
|
79
77
|
res.on('end', () => {
|
|
80
|
-
const raw = Buffer.concat(chunks)
|
|
78
|
+
const raw = Buffer.concat(chunks);
|
|
81
79
|
const status = res.statusCode ?? 0;
|
|
82
80
|
if (status >= 400) {
|
|
81
|
+
const text = raw.toString('utf8');
|
|
83
82
|
logVerboseDebug('CRT response', {
|
|
84
|
-
body:
|
|
83
|
+
body: summarizeTestingDebugResponse(text),
|
|
85
84
|
status,
|
|
86
85
|
});
|
|
87
|
-
|
|
88
|
-
reject(new TestingGatewayError(message, {
|
|
86
|
+
reject(new TestingGatewayError(testingProviderErrorMessage(text, status), {
|
|
89
87
|
retryAfterSeconds: status === 429 ? parseRetryAfterHeader(res.headers['retry-after']) : undefined,
|
|
90
88
|
statusCode: status,
|
|
91
89
|
}));
|
|
@@ -105,7 +103,23 @@ function defaultHttpsRequest(url, method, headers, body, timeoutMs) {
|
|
|
105
103
|
}
|
|
106
104
|
let httpRequestFn = defaultHttpsRequest;
|
|
107
105
|
function requestUrl(request, domain) {
|
|
108
|
-
|
|
106
|
+
if (request.service === 'session') {
|
|
107
|
+
if (request.sessionPath === undefined) {
|
|
108
|
+
throw new TestingGatewayError('CRT session requests require a validated session path.');
|
|
109
|
+
}
|
|
110
|
+
const base = resolveTestingSessionUrl(request.sessionPath, domain);
|
|
111
|
+
if (!/^\/api\/[a-z-]+$/u.test(request.endpoint)) {
|
|
112
|
+
throw new TestingGatewayError('CRT session endpoint has an unexpected format.');
|
|
113
|
+
}
|
|
114
|
+
return new URL(`${base.pathname}${request.endpoint}`, `${base.protocol}//${base.host}`);
|
|
115
|
+
}
|
|
116
|
+
const baseUrl = request.service === 'platform'
|
|
117
|
+
? platformBaseUrl(domain)
|
|
118
|
+
: request.service === 'metrics'
|
|
119
|
+
? metricsBaseUrl(domain)
|
|
120
|
+
: request.service === 'test-data'
|
|
121
|
+
? testDataBaseUrl(domain)
|
|
122
|
+
: paceBaseUrl(domain);
|
|
109
123
|
return new URL(request.endpoint, `${baseUrl}/`);
|
|
110
124
|
}
|
|
111
125
|
function withRequestSuffix(message, request) {
|
|
@@ -114,36 +128,58 @@ function withRequestSuffix(message, request) {
|
|
|
114
128
|
return message;
|
|
115
129
|
return message.endsWith('.') ? `${message} ${suffix}` : `${message}. ${suffix}`;
|
|
116
130
|
}
|
|
117
|
-
function maskSecret(value) {
|
|
118
|
-
if (value.length <= 8) {
|
|
119
|
-
return '<masked>';
|
|
120
|
-
}
|
|
121
|
-
return `${value.slice(0, 4)}...${value.slice(-4)}`;
|
|
122
|
-
}
|
|
123
131
|
function maskTestingHeaders(headers) {
|
|
124
|
-
const masked = {
|
|
125
|
-
|
|
126
|
-
masked[
|
|
132
|
+
const masked = {};
|
|
133
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
134
|
+
masked[key] = /authorization|cookie|csrf|xsrf/iu.test(key) ? TESTING_REDACTED : value;
|
|
127
135
|
}
|
|
128
136
|
return masked;
|
|
129
137
|
}
|
|
130
138
|
async function executeTestingRequest(request, options = {}) {
|
|
131
139
|
const credentials = resolveTestingCredentials(options);
|
|
140
|
+
if (request.service === 'test-data' && request.auth?.mode !== 'session') {
|
|
141
|
+
throw new TestingGatewayError('CRT test-data requests require an in-memory session.');
|
|
142
|
+
}
|
|
143
|
+
if (request.service === 'session' && request.auth?.mode !== 'session') {
|
|
144
|
+
throw new TestingGatewayError('CRT live session requests require an in-memory session.');
|
|
145
|
+
}
|
|
146
|
+
if (request.service === 'test-data' &&
|
|
147
|
+
Object.keys(request.headers ?? {}).some((key) => key.toLowerCase() === 'x-authorization')) {
|
|
148
|
+
throw new TestingGatewayError('CRT test-data requests must not send X-Authorization.');
|
|
149
|
+
}
|
|
150
|
+
if (request.service !== 'test-data' && request.service !== 'session' && request.auth !== undefined) {
|
|
151
|
+
throw new TestingGatewayError('CRT session authentication is restricted to session services.');
|
|
152
|
+
}
|
|
132
153
|
const url = requestUrl(request, credentials.domain);
|
|
133
|
-
const
|
|
154
|
+
const safeUrl = sanitizeTestingUrl(url);
|
|
155
|
+
const requestInfo = { method: request.method, url: safeUrl };
|
|
134
156
|
const transactionId = transactionIdForRequest();
|
|
157
|
+
const authenticationHeaders = {};
|
|
158
|
+
if (request.auth?.mode === 'session') {
|
|
159
|
+
authenticationHeaders.Cookie = request.auth.cookie;
|
|
160
|
+
if (request.auth.xsrfToken !== undefined)
|
|
161
|
+
authenticationHeaders['X-XSRF-TOKEN'] = request.auth.xsrfToken;
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
authenticationHeaders['X-Authorization'] = credentials.pak;
|
|
165
|
+
}
|
|
135
166
|
const headers = {
|
|
136
167
|
...gatewayRefererHeaders(),
|
|
168
|
+
...request.headers,
|
|
137
169
|
'User-Agent': userAgent(),
|
|
138
|
-
|
|
170
|
+
...authenticationHeaders,
|
|
139
171
|
'x-transaction-id': transactionId,
|
|
140
172
|
};
|
|
141
173
|
let raw;
|
|
142
174
|
logVerboseDebug('CRT request', {
|
|
143
|
-
body: request.body
|
|
175
|
+
body: summarizeTestingDebugRequest(request.body, {
|
|
176
|
+
redactValues: request.service === 'session' ||
|
|
177
|
+
request.endpoint.includes('/secrets') ||
|
|
178
|
+
request.endpoint.includes('/user-parameters'),
|
|
179
|
+
}),
|
|
144
180
|
headers: maskTestingHeaders(headers),
|
|
145
181
|
method: request.method,
|
|
146
|
-
url:
|
|
182
|
+
url: safeUrl,
|
|
147
183
|
});
|
|
148
184
|
try {
|
|
149
185
|
scheduleGatewayEventLog({
|
|
@@ -151,30 +187,45 @@ async function executeTestingRequest(request, options = {}) {
|
|
|
151
187
|
method: request.method,
|
|
152
188
|
orgId: credentials.orgId.toString(),
|
|
153
189
|
service: CRT_GATEWAY_EVENT_SERVICE,
|
|
154
|
-
url:
|
|
190
|
+
url: safeUrl,
|
|
155
191
|
});
|
|
156
|
-
raw = await httpRequestFn(url, request.method, headers, request.body, options.timeoutMs ?? TESTING_DEFAULT_TIMEOUT_MS);
|
|
192
|
+
raw = await httpRequestFn(url, request.method, headers, request.body, options.timeoutMs ?? TESTING_DEFAULT_TIMEOUT_MS, request.maxResponseBytes ?? TESTING_MAX_RESPONSE_BYTES);
|
|
157
193
|
}
|
|
158
194
|
catch (error) {
|
|
159
195
|
if (error instanceof TestingGatewayError) {
|
|
160
|
-
|
|
196
|
+
const message = error.statusCode === undefined
|
|
197
|
+
? error.message.startsWith('CRT response exceeded')
|
|
198
|
+
? error.message
|
|
199
|
+
: 'CRT request failed.'
|
|
200
|
+
: testingProviderErrorMessage(JSON.stringify({ message: error.message }), error.statusCode);
|
|
201
|
+
const safeMessage = request.service === 'session' ? message : withRequestSuffix(message, requestInfo);
|
|
202
|
+
throw new TestingGatewayError(safeMessage, {
|
|
161
203
|
request: requestInfo,
|
|
162
204
|
retryAfterSeconds: error.retryAfterSeconds,
|
|
163
205
|
statusCode: error.statusCode,
|
|
164
206
|
});
|
|
165
207
|
}
|
|
166
|
-
|
|
208
|
+
const message = request.service === 'session'
|
|
209
|
+
? 'CRT live request failed before receiving a response.'
|
|
210
|
+
: withRequestSuffix('CRT request failed before receiving a response', requestInfo);
|
|
211
|
+
throw new TestingGatewayError(message, {
|
|
212
|
+
request: requestInfo,
|
|
213
|
+
});
|
|
167
214
|
}
|
|
168
|
-
|
|
169
|
-
|
|
215
|
+
const responseBuffer = typeof raw === 'string' ? Buffer.from(raw, 'utf8') : raw;
|
|
216
|
+
const responseText = responseBuffer.toString('utf8');
|
|
217
|
+
logVerboseDebug('CRT response', summarizeTestingDebugResponse(responseText, request.responseType));
|
|
218
|
+
if (responseBuffer.length === 0)
|
|
170
219
|
return undefined;
|
|
220
|
+
if (request.responseType === 'buffer')
|
|
221
|
+
return responseBuffer;
|
|
171
222
|
if (request.responseType === 'text')
|
|
172
|
-
return
|
|
223
|
+
return responseText;
|
|
173
224
|
try {
|
|
174
|
-
return JSON.parse(
|
|
225
|
+
return JSON.parse(responseText);
|
|
175
226
|
}
|
|
176
227
|
catch {
|
|
177
|
-
throw new TestingGatewayError(withRequestSuffix(
|
|
228
|
+
throw new TestingGatewayError(withRequestSuffix('CRT API returned an unexpected non-JSON response', requestInfo), {
|
|
178
229
|
request: requestInfo,
|
|
179
230
|
});
|
|
180
231
|
}
|
|
@@ -183,6 +234,17 @@ export class TestingGateway {
|
|
|
183
234
|
static async call(request, options = {}) {
|
|
184
235
|
return executeTestingRequest(request, options);
|
|
185
236
|
}
|
|
237
|
+
static async downloadArtifact(location, options = {}) {
|
|
238
|
+
const credentials = resolveTestingCredentials(options);
|
|
239
|
+
const url = resolveTestingArtifactUrl(location, credentials.domain);
|
|
240
|
+
return executeTestingRequest({
|
|
241
|
+
endpoint: `${url.pathname}${url.search}`,
|
|
242
|
+
maxResponseBytes: TESTING_MAX_ARTIFACT_BYTES,
|
|
243
|
+
method: 'GET',
|
|
244
|
+
responseType: 'buffer',
|
|
245
|
+
service: 'pace',
|
|
246
|
+
}, options);
|
|
247
|
+
}
|
|
186
248
|
static async resolveAndValidateDomain(pat, orgId, customDomain) {
|
|
187
249
|
return resolveAndValidateCrtDomain(pat, orgId, customDomain);
|
|
188
250
|
}
|
|
@@ -190,6 +252,74 @@ export class TestingGateway {
|
|
|
190
252
|
await validateCrtCredentials(options);
|
|
191
253
|
}
|
|
192
254
|
}
|
|
255
|
+
export function resolveTestingArtifactUrl(location, configuredDomain) {
|
|
256
|
+
const value = location.trim();
|
|
257
|
+
if (value.length === 0)
|
|
258
|
+
throw new Error('CRT artifact URL is empty.');
|
|
259
|
+
if (value.startsWith('//'))
|
|
260
|
+
throw new Error('CRT artifact URL must be relative or use HTTPS.');
|
|
261
|
+
const expectedHost = `api.${normalizeCrtDomain(configuredDomain)}`;
|
|
262
|
+
const absolute = /^[a-z][a-z\d+.-]*:/iu.test(value);
|
|
263
|
+
if (!absolute) {
|
|
264
|
+
let decodedPath;
|
|
265
|
+
try {
|
|
266
|
+
decodedPath = decodeURIComponent(value.split(/[?#]/u, 1)[0] ?? '');
|
|
267
|
+
}
|
|
268
|
+
catch {
|
|
269
|
+
throw new Error('CRT artifact URL contains invalid path encoding.');
|
|
270
|
+
}
|
|
271
|
+
if (decodedPath.split('/').includes('..')) {
|
|
272
|
+
throw new Error('CRT artifact URL must not contain path traversal.');
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
const url = new URL(value, `https://${expectedHost}/`);
|
|
276
|
+
if (url.protocol !== 'https:' || url.hostname !== expectedHost || url.port.length > 0) {
|
|
277
|
+
throw new Error(`CRT artifact URL must use HTTPS on ${expectedHost}.`);
|
|
278
|
+
}
|
|
279
|
+
if (url.username.length > 0 || url.password.length > 0) {
|
|
280
|
+
throw new Error('CRT artifact URL must not contain credentials.');
|
|
281
|
+
}
|
|
282
|
+
if (absolute && !value.toLowerCase().startsWith('https://')) {
|
|
283
|
+
throw new Error('CRT artifact URL must be relative or use HTTPS.');
|
|
284
|
+
}
|
|
285
|
+
return url;
|
|
286
|
+
}
|
|
287
|
+
export function resolveTestingSessionUrl(location, configuredDomain) {
|
|
288
|
+
const expectedHost = `sessions.${normalizeCrtDomain(configuredDomain)}`;
|
|
289
|
+
const value = location.trim();
|
|
290
|
+
if (value.length === 0 || value.startsWith('//'))
|
|
291
|
+
throw new Error('CRT live session URL is invalid.');
|
|
292
|
+
const url = new URL(value, `${sessionsBaseUrl(configuredDomain)}/`);
|
|
293
|
+
if (url.protocol !== 'https:' ||
|
|
294
|
+
url.hostname !== expectedHost ||
|
|
295
|
+
url.port.length > 0 ||
|
|
296
|
+
url.username.length > 0 ||
|
|
297
|
+
url.password.length > 0) {
|
|
298
|
+
throw new Error(`CRT live session URL must use HTTPS on ${expectedHost}.`);
|
|
299
|
+
}
|
|
300
|
+
if (url.search.length > 0 || url.hash.length > 0) {
|
|
301
|
+
throw new Error('CRT live session URL must not contain query parameters or fragments.');
|
|
302
|
+
}
|
|
303
|
+
let decodedPath;
|
|
304
|
+
try {
|
|
305
|
+
decodedPath = decodeURIComponent(url.pathname);
|
|
306
|
+
}
|
|
307
|
+
catch {
|
|
308
|
+
throw new Error('CRT live session URL contains invalid path encoding.');
|
|
309
|
+
}
|
|
310
|
+
if (decodedPath.split('/').includes('..') || !/^\/live-testing\/\d+\/?$/u.test(decodedPath)) {
|
|
311
|
+
throw new Error('CRT live session URL has an unexpected path.');
|
|
312
|
+
}
|
|
313
|
+
url.pathname = decodedPath.replace(/\/$/u, '');
|
|
314
|
+
return url;
|
|
315
|
+
}
|
|
316
|
+
export function resolveTestingSessionWebSocketUrl(sessionPath, configuredDomain) {
|
|
317
|
+
const session = resolveTestingSessionUrl(sessionPath, configuredDomain);
|
|
318
|
+
const url = new URL(session);
|
|
319
|
+
url.protocol = 'wss:';
|
|
320
|
+
url.pathname = `${url.pathname}/client/websocket`;
|
|
321
|
+
return url;
|
|
322
|
+
}
|
|
193
323
|
export async function validateCrtCredentials(options) {
|
|
194
324
|
const domain = normalizeCrtDomain(options.domain);
|
|
195
325
|
await executeTestingRequest({
|