@datadog/datadog-ci 1.0.0 → 1.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/LICENSE-3rdparty.csv +1 -1
- package/README.md +28 -0
- package/dist/commands/lambda/__tests__/functions/instrument.test.js +2 -2
- package/dist/commands/lambda/__tests__/functions/uninstrument.test.js +2 -2
- package/dist/commands/lambda/__tests__/instrument.test.js +13 -8
- package/dist/commands/lambda/__tests__/prompt.test.js +0 -4
- package/dist/commands/lambda/__tests__/uninstrument.test.js +12 -7
- package/dist/commands/lambda/constants.d.ts +0 -1
- package/dist/commands/lambda/constants.js +2 -27
- package/dist/commands/lambda/functions/commons.js +1 -1
- package/dist/commands/lambda/instrument.js +4 -1
- package/dist/commands/lambda/prompt.d.ts +1 -0
- package/dist/commands/lambda/prompt.js +19 -10
- package/dist/commands/lambda/uninstrument.js +1 -1
- package/dist/commands/synthetics/__tests__/cli.test.js +107 -4
- package/dist/commands/synthetics/__tests__/fixtures.d.ts +2 -1
- package/dist/commands/synthetics/__tests__/fixtures.js +4 -1
- package/dist/commands/synthetics/__tests__/reporters/default.test.js +2 -1
- package/dist/commands/synthetics/__tests__/run-test.test.js +4 -4
- package/dist/commands/synthetics/__tests__/utils.test.js +5 -1
- package/dist/commands/synthetics/api.d.ts +1 -0
- package/dist/commands/synthetics/api.js +5 -3
- package/dist/commands/synthetics/command.js +13 -5
- package/dist/commands/synthetics/errors.d.ts +7 -2
- package/dist/commands/synthetics/errors.js +6 -3
- package/dist/commands/synthetics/index.d.ts +1 -1
- package/dist/commands/synthetics/index.js +2 -1
- package/dist/commands/synthetics/interfaces.d.ts +1 -0
- package/dist/commands/synthetics/reporters/default.d.ts +1 -0
- package/dist/commands/synthetics/reporters/default.js +16 -8
- package/dist/commands/synthetics/run-test.js +9 -15
- package/dist/commands/synthetics/utils.js +21 -9
- package/package.json +12 -3
package/LICENSE-3rdparty.csv
CHANGED
|
@@ -30,7 +30,7 @@ inquirer,import,MIT,Copyright (c) 2012 Simon Boudrias
|
|
|
30
30
|
jest,dev,MIT,Copyright (c) Facebook, Inc. and its affiliates.
|
|
31
31
|
jest-environment-node,dev,MIT,Copyright (c) Facebook, Inc. and its affiliates.
|
|
32
32
|
jest-matcher-specific-error,dev,MIT,Copyright (c) 2020 Daniel Hreben
|
|
33
|
-
|
|
33
|
+
pkg,dev,MIT,Copyright (c) 2021 Vercel, Inc.
|
|
34
34
|
prettier,dev,MIT,Copyright © James Long and contributors
|
|
35
35
|
proxy,dev,MIT,Copyright (c) 2013 Nathan Rajlich <nathan@tootallnate.net>
|
|
36
36
|
proxy-agent,import,MIT,Copyright (c) 2013 Nathan Rajlich <nathan@tootallnate.net>
|
package/README.md
CHANGED
|
@@ -29,6 +29,34 @@ npm install -g @datadog/datadog-ci
|
|
|
29
29
|
yarn global add @datadog/datadog-ci
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
+
### Standalone binary (**beta**)
|
|
33
|
+
|
|
34
|
+
If installing nodejs in the CI is an issue, standalone binaries are provided with [releases](https://github.com/DataDog/datadog-ci/releases). Only _linux-x64_, _darwin-x64_ (macOS) and _win-x64_ (Windows) are supported at the time. **These standalone binaries are in _beta_ and their stability is not guaranteed**. To install:
|
|
35
|
+
|
|
36
|
+
#### Linux
|
|
37
|
+
|
|
38
|
+
```sh
|
|
39
|
+
curl -L --fail "https://github.com/DataDog/datadog-ci/releases/latest/download/datadog-ci_linux-x64" --output "/usr/local/bin/datadog-ci" && chmod +x /usr/local/bin/datadog-ci
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
#### MacOS
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
curl -L --fail "https://github.com/DataDog/datadog-ci/releases/latest/download/datadog-ci_darwin-x64" --output "/usr/local/bin/datadog-ci" && chmod +x /usr/local/bin/datadog-ci
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
#### Windows
|
|
49
|
+
|
|
50
|
+
```sh
|
|
51
|
+
Invoke-WebRequest -Uri "https://github.com/DataDog/datadog-ci/releases/latest/download/datadog-ci_win-x64.exe" -OutFile "datadog-ci.exe"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Then you can run `datadog-ci` commands normally:
|
|
55
|
+
|
|
56
|
+
```sh
|
|
57
|
+
datadog-ci version
|
|
58
|
+
```
|
|
59
|
+
|
|
32
60
|
## Usage
|
|
33
61
|
|
|
34
62
|
```bash
|
|
@@ -741,7 +741,7 @@ describe('instrument', () => {
|
|
|
741
741
|
test('fails when retry count is exceeded', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
742
742
|
const makeMockLambdaListFunctionsError = () => ({
|
|
743
743
|
listFunctions: jest.fn().mockImplementation((args) => ({
|
|
744
|
-
promise: () => Promise.reject(),
|
|
744
|
+
promise: () => Promise.reject('ListFunctionsError'),
|
|
745
745
|
})),
|
|
746
746
|
});
|
|
747
747
|
const lambda = makeMockLambdaListFunctionsError();
|
|
@@ -753,7 +753,7 @@ describe('instrument', () => {
|
|
|
753
753
|
mergeXrayTraces: false,
|
|
754
754
|
tracingEnabled: false,
|
|
755
755
|
};
|
|
756
|
-
yield expect(instrument_1.getInstrumentedFunctionConfigsFromRegEx(lambda, cloudWatch, 'us-east-1', 'fake-pattern', settings)).rejects.toStrictEqual(new Error('Max retry count exceeded.'));
|
|
756
|
+
yield expect(instrument_1.getInstrumentedFunctionConfigsFromRegEx(lambda, cloudWatch, 'us-east-1', 'fake-pattern', settings)).rejects.toStrictEqual(new Error('Max retry count exceeded. ListFunctionsError'));
|
|
757
757
|
}));
|
|
758
758
|
});
|
|
759
759
|
});
|
|
@@ -394,12 +394,12 @@ describe('uninstrument', () => {
|
|
|
394
394
|
test('fails when retry count is exceeded', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
395
395
|
const makeMockLambdaListFunctionsError = () => ({
|
|
396
396
|
listFunctions: jest.fn().mockImplementation((args) => ({
|
|
397
|
-
promise: () => Promise.reject(),
|
|
397
|
+
promise: () => Promise.reject('ListFunctionsError'),
|
|
398
398
|
})),
|
|
399
399
|
});
|
|
400
400
|
const lambda = makeMockLambdaListFunctionsError();
|
|
401
401
|
const cloudWatch = fixtures_1.makeMockCloudWatchLogs({});
|
|
402
|
-
yield expect(uninstrument_1.getUninstrumentedFunctionConfigsFromRegEx(lambda, cloudWatch, 'fake-pattern', undefined)).rejects.toStrictEqual(new Error('Max retry count exceeded.'));
|
|
402
|
+
yield expect(uninstrument_1.getUninstrumentedFunctionConfigsFromRegEx(lambda, cloudWatch, 'fake-pattern', undefined)).rejects.toStrictEqual(new Error('Max retry count exceeded. ListFunctionsError'));
|
|
403
403
|
}));
|
|
404
404
|
});
|
|
405
405
|
});
|
|
@@ -853,7 +853,8 @@ TagResource -> arn:aws:lambda:us-east-1:123456789012:function:lambda-hello-world
|
|
|
853
853
|
const output = context.stdout.toString();
|
|
854
854
|
expect(code).toBe(0);
|
|
855
855
|
expect(output).toMatchInlineSnapshot(`
|
|
856
|
-
"${chalk_1.bold(chalk_1.yellow('[!]'))} No
|
|
856
|
+
"${chalk_1.bold(chalk_1.yellow('[!]'))} No AWS credentials found, let's set them up! Or you can re-run the command and supply the AWS credentials in the same way when you invoke the AWS CLI.
|
|
857
|
+
${chalk_1.bold(chalk_1.yellow('[!]'))} Configure AWS region.
|
|
857
858
|
${chalk_1.bold(chalk_1.yellow('[!]'))} Configure Datadog settings.
|
|
858
859
|
Fetching Lambda functions, this might take a while.
|
|
859
860
|
${chalk_1.bold(chalk_1.yellow('[Warning]'))} The environment, service and version tags have not been configured. Learn more about Datadog unified service tagging: ${chalk_1.underline(chalk_1.blueBright('https://docs.datadoghq.com/getting_started/tagging/unified_service_tagging/#serverless-environment.'))}
|
|
@@ -974,7 +975,8 @@ ${chalk_1.yellow('[!]')} Instrumenting functions.
|
|
|
974
975
|
const output = context.stdout.toString();
|
|
975
976
|
expect(code).toBe(0);
|
|
976
977
|
expect(output).toMatchInlineSnapshot(`
|
|
977
|
-
"${chalk_1.bold(chalk_1.yellow('[!]'))} No
|
|
978
|
+
"${chalk_1.bold(chalk_1.yellow('[!]'))} No AWS credentials found, let's set them up! Or you can re-run the command and supply the AWS credentials in the same way when you invoke the AWS CLI.
|
|
979
|
+
${chalk_1.bold(chalk_1.yellow('[!]'))} Configure AWS region.
|
|
978
980
|
${chalk_1.bold(chalk_1.yellow('[!]'))} Configure Datadog settings.
|
|
979
981
|
${chalk_1.bold(chalk_1.yellow('[Warning]'))} The environment, service and version tags have not been configured. Learn more about Datadog unified service tagging: ${chalk_1.underline(chalk_1.blueBright('https://docs.datadoghq.com/getting_started/tagging/unified_service_tagging/#serverless-environment.'))}
|
|
980
982
|
${chalk_1.bold(chalk_1.yellow('[Warning]'))} Instrument your ${chalk_1.hex('#FF9900').bold('Lambda')} functions in a dev or staging environment first. Should the instrumentation result be unsatisfactory, run \`${chalk_1.bold('uninstrument')}\` with the same arguments to revert the changes.\n
|
|
@@ -1047,7 +1049,7 @@ ${chalk_1.yellow('[!]')} Instrumenting functions.
|
|
|
1047
1049
|
const output = context.stdout.toString();
|
|
1048
1050
|
expect(code).toBe(1);
|
|
1049
1051
|
expect(output).toMatchInlineSnapshot(`
|
|
1050
|
-
"${chalk_1.bold(chalk_1.yellow('[!]'))} No
|
|
1052
|
+
"${chalk_1.bold(chalk_1.yellow('[!]'))} No AWS credentials found, let's set them up! Or you can re-run the command and supply the AWS credentials in the same way when you invoke the AWS CLI.
|
|
1051
1053
|
${chalk_1.red('[Error]')} Unexpected error
|
|
1052
1054
|
"
|
|
1053
1055
|
`);
|
|
@@ -1066,7 +1068,8 @@ ${chalk_1.red('[Error]')} Unexpected error
|
|
|
1066
1068
|
const output = context.stdout.toString();
|
|
1067
1069
|
expect(code).toBe(1);
|
|
1068
1070
|
expect(output).toMatchInlineSnapshot(`
|
|
1069
|
-
"${chalk_1.bold(chalk_1.yellow('[!]'))} Configure
|
|
1071
|
+
"${chalk_1.bold(chalk_1.yellow('[!]'))} Configure AWS region.
|
|
1072
|
+
${chalk_1.bold(chalk_1.yellow('[!]'))} Configure Datadog settings.
|
|
1070
1073
|
${chalk_1.red('[Error]')} Unexpected error
|
|
1071
1074
|
"
|
|
1072
1075
|
`);
|
|
@@ -1087,7 +1090,8 @@ ${chalk_1.red('[Error]')} Unexpected error
|
|
|
1087
1090
|
const output = context.stdout.toString();
|
|
1088
1091
|
expect(code).toBe(1);
|
|
1089
1092
|
expect(output).toMatchInlineSnapshot(`
|
|
1090
|
-
"
|
|
1093
|
+
"${chalk_1.bold(chalk_1.yellow('[!]'))} Configure AWS region.
|
|
1094
|
+
Fetching Lambda functions, this might take a while.
|
|
1091
1095
|
${chalk_1.red('[Error]')} Couldn't find any Lambda functions in the specified region.
|
|
1092
1096
|
"
|
|
1093
1097
|
`);
|
|
@@ -1102,7 +1106,7 @@ ${chalk_1.red('[Error]')} Couldn't find any Lambda functions in the specified re
|
|
|
1102
1106
|
};
|
|
1103
1107
|
fs.readFile.mockImplementation((a, b, callback) => callback({ code: 'ENOENT' }));
|
|
1104
1108
|
aws_sdk_1.Lambda.mockImplementation(() => ({
|
|
1105
|
-
listFunctions: jest.fn().mockImplementation(() => ({ promise: () => Promise.reject('
|
|
1109
|
+
listFunctions: jest.fn().mockImplementation(() => ({ promise: () => Promise.reject('ListFunctionsError') })),
|
|
1106
1110
|
}));
|
|
1107
1111
|
const cli = fixtures_1.makeCli();
|
|
1108
1112
|
const context = fixtures_1.createMockContext();
|
|
@@ -1110,8 +1114,9 @@ ${chalk_1.red('[Error]')} Couldn't find any Lambda functions in the specified re
|
|
|
1110
1114
|
const output = context.stdout.toString();
|
|
1111
1115
|
expect(code).toBe(1);
|
|
1112
1116
|
expect(output).toMatchInlineSnapshot(`
|
|
1113
|
-
"
|
|
1114
|
-
|
|
1117
|
+
"${chalk_1.bold(chalk_1.yellow('[!]'))} Configure AWS region.
|
|
1118
|
+
Fetching Lambda functions, this might take a while.
|
|
1119
|
+
${chalk_1.red('[Error]')} Couldn't fetch Lambda functions. Error: Max retry count exceeded. ListFunctionsError
|
|
1115
1120
|
"
|
|
1116
1121
|
`);
|
|
1117
1122
|
}));
|
|
@@ -80,25 +80,21 @@ describe('prompt', () => {
|
|
|
80
80
|
inquirer_1.prompt.mockImplementation(() => Promise.resolve({
|
|
81
81
|
[constants_1.AWS_ACCESS_KEY_ID_ENV_VAR]: fixtures_1.mockAwsAccessKeyId,
|
|
82
82
|
[constants_1.AWS_SECRET_ACCESS_KEY_ENV_VAR]: fixtures_1.mockAwsSecretAccessKey,
|
|
83
|
-
[constants_1.AWS_DEFAULT_REGION_ENV_VAR]: 'sa-east-1',
|
|
84
83
|
}));
|
|
85
84
|
yield prompt_1.requestAWSCredentials();
|
|
86
85
|
expect(process.env[constants_1.AWS_ACCESS_KEY_ID_ENV_VAR]).toBe(fixtures_1.mockAwsAccessKeyId);
|
|
87
86
|
expect(process.env[constants_1.AWS_SECRET_ACCESS_KEY_ENV_VAR]).toBe(fixtures_1.mockAwsSecretAccessKey);
|
|
88
|
-
expect(process.env[constants_1.AWS_DEFAULT_REGION_ENV_VAR]).toBe('sa-east-1');
|
|
89
87
|
}));
|
|
90
88
|
test('sets the AWS credentials with session token as environment variables', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
91
89
|
;
|
|
92
90
|
inquirer_1.prompt.mockImplementation(() => Promise.resolve({
|
|
93
91
|
[constants_1.AWS_ACCESS_KEY_ID_ENV_VAR]: fixtures_1.mockAwsAccessKeyId,
|
|
94
92
|
[constants_1.AWS_SECRET_ACCESS_KEY_ENV_VAR]: fixtures_1.mockAwsSecretAccessKey,
|
|
95
|
-
[constants_1.AWS_DEFAULT_REGION_ENV_VAR]: 'sa-east-1',
|
|
96
93
|
[constants_1.AWS_SESSION_TOKEN_ENV_VAR]: 'some-session-token',
|
|
97
94
|
}));
|
|
98
95
|
yield prompt_1.requestAWSCredentials();
|
|
99
96
|
expect(process.env[constants_1.AWS_ACCESS_KEY_ID_ENV_VAR]).toBe(fixtures_1.mockAwsAccessKeyId);
|
|
100
97
|
expect(process.env[constants_1.AWS_SECRET_ACCESS_KEY_ENV_VAR]).toBe(fixtures_1.mockAwsSecretAccessKey);
|
|
101
|
-
expect(process.env[constants_1.AWS_DEFAULT_REGION_ENV_VAR]).toBe('sa-east-1');
|
|
102
98
|
expect(process.env[constants_1.AWS_SESSION_TOKEN_ENV_VAR]).toBe('some-session-token');
|
|
103
99
|
}));
|
|
104
100
|
test('throws error when something unexpected happens while prompting', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -257,15 +257,20 @@ UpdateFunctionConfiguration -> arn:aws:lambda:us-east-1:000000000000:function:un
|
|
|
257
257
|
test('aborts if the the aws-sdk fails', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
258
258
|
;
|
|
259
259
|
fs.readFile.mockImplementation((a, b, callback) => callback({}));
|
|
260
|
-
aws_sdk_1.Lambda.mockImplementation(() => ({
|
|
260
|
+
aws_sdk_1.Lambda.mockImplementation(() => ({
|
|
261
|
+
listFunctions: jest.fn().mockImplementation(() => ({ promise: () => Promise.reject('ListFunctionsError') })),
|
|
262
|
+
}));
|
|
261
263
|
process.env = {};
|
|
264
|
+
aws_sdk_1.Lambda.mockImplementation(() => ({
|
|
265
|
+
listFunctions: jest.fn().mockImplementation(() => ({ promise: () => Promise.reject('ListFunctionsError') })),
|
|
266
|
+
}));
|
|
262
267
|
const command = fixtures_1.createCommand(uninstrument_1.UninstrumentCommand);
|
|
263
268
|
command['region'] = 'ap-southeast-1';
|
|
264
269
|
command['regExPattern'] = 'my-function';
|
|
265
270
|
const code = yield command['execute']();
|
|
266
271
|
const output = command.context.stdout.toString();
|
|
267
272
|
expect(code).toBe(1);
|
|
268
|
-
expect(output).toMatch(`Fetching Lambda functions, this might take a while.\n${chalk_1.red('[Error]')} Couldn't fetch Lambda functions. Error: Max retry count exceeded
|
|
273
|
+
expect(output).toMatch(`Fetching Lambda functions, this might take a while.\n${chalk_1.red('[Error]')} Couldn't fetch Lambda functions. Error: Max retry count exceeded. ListFunctionsError\n`);
|
|
269
274
|
}));
|
|
270
275
|
test('uninstrument multiple functions interactively', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
271
276
|
;
|
|
@@ -357,7 +362,7 @@ UpdateFunctionConfiguration -> arn:aws:lambda:us-east-1:000000000000:function:un
|
|
|
357
362
|
const output = context.stdout.toString();
|
|
358
363
|
expect(code).toBe(0);
|
|
359
364
|
expect(output).toMatchInlineSnapshot(`
|
|
360
|
-
"${chalk_1.bold(chalk_1.yellow('[!]'))} No
|
|
365
|
+
"${chalk_1.bold(chalk_1.yellow('[!]'))} No AWS credentials found, let's set them up! Or you can re-run the command and supply the AWS credentials in the same way when you invoke the AWS CLI.
|
|
361
366
|
Fetching Lambda functions, this might take a while.\n
|
|
362
367
|
${chalk_1.bold(chalk_1.yellow('[!]'))} Functions to be updated:
|
|
363
368
|
\t- ${chalk_1.bold('arn:aws:lambda:sa-east-1:123456789012:function:lambda-hello-world')}
|
|
@@ -486,7 +491,7 @@ ${chalk_1.yellow('[!]')} Uninstrumenting functions.
|
|
|
486
491
|
const output = context.stdout.toString();
|
|
487
492
|
expect(code).toBe(0);
|
|
488
493
|
expect(output).toMatchInlineSnapshot(`
|
|
489
|
-
"${chalk_1.bold(chalk_1.yellow('[!]'))} No
|
|
494
|
+
"${chalk_1.bold(chalk_1.yellow('[!]'))} No AWS credentials found, let's set them up! Or you can re-run the command and supply the AWS credentials in the same way when you invoke the AWS CLI.\n
|
|
490
495
|
${chalk_1.bold(chalk_1.yellow('[!]'))} Functions to be updated:
|
|
491
496
|
\t- ${chalk_1.bold('arn:aws:lambda:sa-east-1:123456789012:function:lambda-hello-world')}
|
|
492
497
|
\t- ${chalk_1.bold('arn:aws:lambda:sa-east-1:123456789012:function:lambda-hello-world-2')}\n
|
|
@@ -526,7 +531,7 @@ ${chalk_1.yellow('[!]')} Uninstrumenting functions.
|
|
|
526
531
|
const output = context.stdout.toString();
|
|
527
532
|
expect(code).toBe(1);
|
|
528
533
|
expect(output).toMatchInlineSnapshot(`
|
|
529
|
-
"${chalk_1.bold(chalk_1.yellow('[!]'))} No
|
|
534
|
+
"${chalk_1.bold(chalk_1.yellow('[!]'))} No AWS credentials found, let's set them up! Or you can re-run the command and supply the AWS credentials in the same way when you invoke the AWS CLI.
|
|
530
535
|
${chalk_1.red('[Error]')} Unexpected error
|
|
531
536
|
"
|
|
532
537
|
`);
|
|
@@ -558,7 +563,7 @@ ${chalk_1.red('[Error]')} Couldn't find any Lambda functions in the specified re
|
|
|
558
563
|
};
|
|
559
564
|
fs.readFile.mockImplementation((a, b, callback) => callback({ code: 'ENOENT' }));
|
|
560
565
|
aws_sdk_1.Lambda.mockImplementation(() => ({
|
|
561
|
-
listFunctions: jest.fn().mockImplementation(() => ({ promise: () => Promise.reject('
|
|
566
|
+
listFunctions: jest.fn().mockImplementation(() => ({ promise: () => Promise.reject('ListFunctionsError') })),
|
|
562
567
|
}));
|
|
563
568
|
const cli = fixtures_1.makeCli();
|
|
564
569
|
const context = fixtures_1.createMockContext();
|
|
@@ -567,7 +572,7 @@ ${chalk_1.red('[Error]')} Couldn't find any Lambda functions in the specified re
|
|
|
567
572
|
expect(code).toBe(1);
|
|
568
573
|
expect(output).toMatchInlineSnapshot(`
|
|
569
574
|
"Fetching Lambda functions, this might take a while.
|
|
570
|
-
${chalk_1.red('[Error]')} Couldn't fetch Lambda functions. Error: Max retry count exceeded.
|
|
575
|
+
${chalk_1.red('[Error]')} Couldn't fetch Lambda functions. Error: Max retry count exceeded. ListFunctionsError
|
|
571
576
|
"
|
|
572
577
|
`);
|
|
573
578
|
}));
|
|
@@ -41,7 +41,6 @@ export declare const ARM_LAYER_SUFFIX = "-ARM";
|
|
|
41
41
|
export declare const PYTHON_HANDLER_LOCATION = "datadog_lambda.handler.handler";
|
|
42
42
|
export declare const NODE_HANDLER_LOCATION = "/opt/nodejs/node_modules/datadog-lambda-js/handler.handler";
|
|
43
43
|
export declare const SITES: string[];
|
|
44
|
-
export declare const AWS_REGIONS: string[];
|
|
45
44
|
export declare const DEFAULT_LAYER_AWS_ACCOUNT = "464622532012";
|
|
46
45
|
export declare const GOVCLOUD_LAYER_AWS_ACCOUNT = "002406178527";
|
|
47
46
|
export declare const SUBSCRIPTION_FILTER_NAME = "datadog-ci-filter";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.EXTRA_TAGS_REG_EXP = exports.MAX_LAMBDA_STATE_CHECK_ATTEMPTS = exports.LIST_FUNCTIONS_MAX_RETRY_COUNT = exports.AWS_SESSION_TOKEN_ENV_VAR = exports.AWS_DEFAULT_REGION_ENV_VAR = exports.AWS_SECRET_ACCESS_KEY_ENV_VAR = exports.AWS_ACCESS_KEY_ID_ENV_VAR = exports.CI_KMS_API_KEY_ENV_VAR = exports.CI_API_KEY_SECRET_ARN_ENV_VAR = exports.CI_API_KEY_ENV_VAR = exports.CI_SITE_ENV_VAR = exports.DOTNET_TRACER_HOME_ENV_VAR = exports.PROFILER_PATH_ENV_VAR = exports.PROFILER_ENV_VAR = exports.ENABLE_PROFILING_ENV_VAR = exports.CAPTURE_LAMBDA_PAYLOAD_ENV_VAR = exports.EXTRA_TAGS_ENV_VAR = exports.ENVIRONMENT_ENV_VAR = exports.VERSION_ENV_VAR = exports.SERVICE_ENV_VAR = exports.LAMBDA_HANDLER_ENV_VAR = exports.LOG_LEVEL_ENV_VAR = exports.FLUSH_TO_LOG_ENV_VAR = exports.MERGE_XRAY_TRACES_ENV_VAR = exports.TRACE_ENABLED_ENV_VAR = exports.SITE_ENV_VAR = exports.KMS_API_KEY_ENV_VAR = exports.API_KEY_SECRET_ARN_ENV_VAR = exports.API_KEY_ENV_VAR = exports.DD_DOTNET_TRACER_HOME = exports.CORECLR_PROFILER_PATH = exports.CORECLR_PROFILER = exports.CORECLR_ENABLE_PROFILING = exports.TAG_VERSION_NAME = exports.SUBSCRIPTION_FILTER_NAME = exports.GOVCLOUD_LAYER_AWS_ACCOUNT = exports.DEFAULT_LAYER_AWS_ACCOUNT = exports.
|
|
4
|
-
exports.DATADOG_APP_KEY_REG_EXP = exports.DATADOG_API_KEY_REG_EXP = exports.AWS_SECRET_ACCESS_KEY_REG_EXP =
|
|
3
|
+
exports.AWS_ACCESS_KEY_ID_REG_EXP = exports.EXTRA_TAGS_REG_EXP = exports.MAX_LAMBDA_STATE_CHECK_ATTEMPTS = exports.LIST_FUNCTIONS_MAX_RETRY_COUNT = exports.AWS_SESSION_TOKEN_ENV_VAR = exports.AWS_DEFAULT_REGION_ENV_VAR = exports.AWS_SECRET_ACCESS_KEY_ENV_VAR = exports.AWS_ACCESS_KEY_ID_ENV_VAR = exports.CI_KMS_API_KEY_ENV_VAR = exports.CI_API_KEY_SECRET_ARN_ENV_VAR = exports.CI_API_KEY_ENV_VAR = exports.CI_SITE_ENV_VAR = exports.DOTNET_TRACER_HOME_ENV_VAR = exports.PROFILER_PATH_ENV_VAR = exports.PROFILER_ENV_VAR = exports.ENABLE_PROFILING_ENV_VAR = exports.CAPTURE_LAMBDA_PAYLOAD_ENV_VAR = exports.EXTRA_TAGS_ENV_VAR = exports.ENVIRONMENT_ENV_VAR = exports.VERSION_ENV_VAR = exports.SERVICE_ENV_VAR = exports.LAMBDA_HANDLER_ENV_VAR = exports.LOG_LEVEL_ENV_VAR = exports.FLUSH_TO_LOG_ENV_VAR = exports.MERGE_XRAY_TRACES_ENV_VAR = exports.TRACE_ENABLED_ENV_VAR = exports.SITE_ENV_VAR = exports.KMS_API_KEY_ENV_VAR = exports.API_KEY_SECRET_ARN_ENV_VAR = exports.API_KEY_ENV_VAR = exports.DD_DOTNET_TRACER_HOME = exports.CORECLR_PROFILER_PATH = exports.CORECLR_PROFILER = exports.CORECLR_ENABLE_PROFILING = exports.TAG_VERSION_NAME = exports.SUBSCRIPTION_FILTER_NAME = exports.GOVCLOUD_LAYER_AWS_ACCOUNT = exports.DEFAULT_LAYER_AWS_ACCOUNT = exports.SITES = exports.NODE_HANDLER_LOCATION = exports.PYTHON_HANDLER_LOCATION = exports.ARM_LAYER_SUFFIX = exports.ARM64_ARCHITECTURE = exports.ARM_LAYERS = exports.RUNTIME_LOOKUP = exports.RuntimeType = exports.LAYER_LOOKUP = exports.DOTNET_RUNTIME = exports.EXTENSION_LAYER_KEY = exports.DD_LAMBDA_EXTENSION_LAYER_NAME = void 0;
|
|
4
|
+
exports.DATADOG_APP_KEY_REG_EXP = exports.DATADOG_API_KEY_REG_EXP = exports.AWS_SECRET_ACCESS_KEY_REG_EXP = void 0;
|
|
5
5
|
exports.DD_LAMBDA_EXTENSION_LAYER_NAME = 'Datadog-Extension';
|
|
6
6
|
exports.EXTENSION_LAYER_KEY = 'extension';
|
|
7
7
|
exports.DOTNET_RUNTIME = 'dotnetcore3.1';
|
|
@@ -50,31 +50,6 @@ exports.SITES = [
|
|
|
50
50
|
'us5.datadoghq.com',
|
|
51
51
|
'ddog-gov.com',
|
|
52
52
|
];
|
|
53
|
-
exports.AWS_REGIONS = [
|
|
54
|
-
'us-east-1',
|
|
55
|
-
'us-east-2',
|
|
56
|
-
'us-west-1',
|
|
57
|
-
'us-west-2',
|
|
58
|
-
'af-south-1',
|
|
59
|
-
'ap-east-1',
|
|
60
|
-
'ap-south-1',
|
|
61
|
-
'ap-northeast-3',
|
|
62
|
-
'ap-northeast-2',
|
|
63
|
-
'ap-southeast-1',
|
|
64
|
-
'ap-southeast-2',
|
|
65
|
-
'ap-northeast-1',
|
|
66
|
-
'ca-central-1',
|
|
67
|
-
'eu-central-1',
|
|
68
|
-
'eu-west-1',
|
|
69
|
-
'eu-west-2',
|
|
70
|
-
'eu-south-1',
|
|
71
|
-
'eu-west-3',
|
|
72
|
-
'eu-north-1',
|
|
73
|
-
'me-south-1',
|
|
74
|
-
'sa-east-1',
|
|
75
|
-
'us-gov-east-1',
|
|
76
|
-
'us-gov-west-1',
|
|
77
|
-
];
|
|
78
53
|
exports.DEFAULT_LAYER_AWS_ACCOUNT = '464622532012';
|
|
79
54
|
exports.GOVCLOUD_LAYER_AWS_ACCOUNT = '002406178527';
|
|
80
55
|
exports.SUBSCRIPTION_FILTER_NAME = 'datadog-ci-filter';
|
|
@@ -206,7 +206,7 @@ const getLambdaFunctionConfigsFromRegex = (lambda, pattern) => __awaiter(void 0,
|
|
|
206
206
|
catch (e) {
|
|
207
207
|
retryCount++;
|
|
208
208
|
if (retryCount > constants_1.LIST_FUNCTIONS_MAX_RETRY_COUNT) {
|
|
209
|
-
throw Error(
|
|
209
|
+
throw Error(`Max retry count exceeded. ${e}`);
|
|
210
210
|
}
|
|
211
211
|
}
|
|
212
212
|
}
|
|
@@ -42,9 +42,12 @@ class InstrumentCommand extends clipanion_1.Command {
|
|
|
42
42
|
if (this.interactive) {
|
|
43
43
|
try {
|
|
44
44
|
if (commons_1.isMissingAWSCredentials()) {
|
|
45
|
-
this.context.stdout.write(`${chalk_1.bold(chalk_1.yellow('[!]'))} No
|
|
45
|
+
this.context.stdout.write(`${chalk_1.bold(chalk_1.yellow('[!]'))} No AWS credentials found, let's set them up! Or you can re-run the command and supply the AWS credentials in the same way when you invoke the AWS CLI.\n`);
|
|
46
46
|
yield prompt_1.requestAWSCredentials();
|
|
47
47
|
}
|
|
48
|
+
// Always ask for region since the user may not want to use the default
|
|
49
|
+
this.context.stdout.write(`${chalk_1.bold(chalk_1.yellow('[!]'))} Configure AWS region.\n`);
|
|
50
|
+
yield prompt_1.requestAWSRegion(process.env[constants_1.AWS_DEFAULT_REGION_ENV_VAR]);
|
|
48
51
|
if (commons_1.isMissingDatadogEnvVars()) {
|
|
49
52
|
this.context.stdout.write(`${chalk_1.bold(chalk_1.yellow('[!]'))} Configure Datadog settings.\n`);
|
|
50
53
|
yield prompt_1.requestDatadogEnvVars();
|
|
@@ -4,6 +4,7 @@ export declare const datadogEnvVarsQuestions: (datadogApiKeyType: Record<string,
|
|
|
4
4
|
export declare const confirmationQuestion: (message: string) => ConfirmQuestion;
|
|
5
5
|
export declare const functionSelectionQuestion: (functionNames: string[]) => CheckboxQuestion;
|
|
6
6
|
export declare const requestAWSCredentials: () => Promise<void>;
|
|
7
|
+
export declare const requestAWSRegion: (defaultRegion?: string | undefined) => Promise<void>;
|
|
7
8
|
export declare const requestDatadogEnvVars: () => Promise<void>;
|
|
8
9
|
export declare const requestChangesConfirmation: (message: string) => Promise<any>;
|
|
9
10
|
export declare const requestFunctionSelection: (functionNames: string[]) => Promise<any>;
|
|
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.requestFunctionSelection = exports.requestChangesConfirmation = exports.requestDatadogEnvVars = exports.requestAWSCredentials = exports.functionSelectionQuestion = exports.confirmationQuestion = exports.datadogEnvVarsQuestions = exports.datadogApiKeyTypeQuestion = void 0;
|
|
12
|
+
exports.requestFunctionSelection = exports.requestChangesConfirmation = exports.requestDatadogEnvVars = exports.requestAWSRegion = exports.requestAWSCredentials = exports.functionSelectionQuestion = exports.confirmationQuestion = exports.datadogEnvVarsQuestions = exports.datadogApiKeyTypeQuestion = void 0;
|
|
13
13
|
const chalk_1 = require("chalk");
|
|
14
14
|
const inquirer_1 = require("inquirer");
|
|
15
15
|
const constants_1 = require("./constants");
|
|
@@ -47,15 +47,13 @@ const awsCredentialsQuestions = [
|
|
|
47
47
|
name: constants_1.AWS_SESSION_TOKEN_ENV_VAR,
|
|
48
48
|
type: 'password',
|
|
49
49
|
},
|
|
50
|
-
{
|
|
51
|
-
// AWS_DEFAULT_REGION
|
|
52
|
-
choices: constants_1.AWS_REGIONS,
|
|
53
|
-
default: 0,
|
|
54
|
-
message: 'Select an AWS region:',
|
|
55
|
-
name: constants_1.AWS_DEFAULT_REGION_ENV_VAR,
|
|
56
|
-
type: 'list',
|
|
57
|
-
},
|
|
58
50
|
];
|
|
51
|
+
const awsRegionQuestion = (defaultRegion) => ({
|
|
52
|
+
default: defaultRegion,
|
|
53
|
+
message: 'Which AWS region (e.g., us-east-1) your Lambda functions are deployed?',
|
|
54
|
+
name: constants_1.AWS_DEFAULT_REGION_ENV_VAR,
|
|
55
|
+
type: 'input',
|
|
56
|
+
});
|
|
59
57
|
const datadogApiKeyTypeQuestion = (datadogSite) => ({
|
|
60
58
|
choices: [
|
|
61
59
|
{
|
|
@@ -131,7 +129,6 @@ const requestAWSCredentials = () => __awaiter(void 0, void 0, void 0, function*
|
|
|
131
129
|
const awsCredentialsAnswers = yield inquirer_1.prompt(awsCredentialsQuestions);
|
|
132
130
|
process.env[constants_1.AWS_ACCESS_KEY_ID_ENV_VAR] = awsCredentialsAnswers[constants_1.AWS_ACCESS_KEY_ID_ENV_VAR];
|
|
133
131
|
process.env[constants_1.AWS_SECRET_ACCESS_KEY_ENV_VAR] = awsCredentialsAnswers[constants_1.AWS_SECRET_ACCESS_KEY_ENV_VAR];
|
|
134
|
-
process.env[constants_1.AWS_DEFAULT_REGION_ENV_VAR] = awsCredentialsAnswers[constants_1.AWS_DEFAULT_REGION_ENV_VAR];
|
|
135
132
|
if (awsCredentialsAnswers[constants_1.AWS_SESSION_TOKEN_ENV_VAR] !== undefined) {
|
|
136
133
|
process.env[constants_1.AWS_SESSION_TOKEN_ENV_VAR] = awsCredentialsAnswers[constants_1.AWS_SESSION_TOKEN_ENV_VAR];
|
|
137
134
|
}
|
|
@@ -143,6 +140,18 @@ const requestAWSCredentials = () => __awaiter(void 0, void 0, void 0, function*
|
|
|
143
140
|
}
|
|
144
141
|
});
|
|
145
142
|
exports.requestAWSCredentials = requestAWSCredentials;
|
|
143
|
+
const requestAWSRegion = (defaultRegion) => __awaiter(void 0, void 0, void 0, function* () {
|
|
144
|
+
try {
|
|
145
|
+
const awsRegionAnswer = yield inquirer_1.prompt(awsRegionQuestion(defaultRegion));
|
|
146
|
+
process.env[constants_1.AWS_DEFAULT_REGION_ENV_VAR] = awsRegionAnswer[constants_1.AWS_DEFAULT_REGION_ENV_VAR];
|
|
147
|
+
}
|
|
148
|
+
catch (e) {
|
|
149
|
+
if (e instanceof Error) {
|
|
150
|
+
throw Error(`Couldn't set AWS region. ${e.message}`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
exports.requestAWSRegion = requestAWSRegion;
|
|
146
155
|
const requestDatadogEnvVars = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
147
156
|
try {
|
|
148
157
|
const datadogSiteAnswer = yield inquirer_1.prompt(datadogSiteQuestion);
|
|
@@ -38,7 +38,7 @@ class UninstrumentCommand extends clipanion_1.Command {
|
|
|
38
38
|
if (this.interactive) {
|
|
39
39
|
try {
|
|
40
40
|
if (commons_1.isMissingAWSCredentials()) {
|
|
41
|
-
this.context.stdout.write(`${chalk_1.bold(chalk_1.yellow('[!]'))} No
|
|
41
|
+
this.context.stdout.write(`${chalk_1.bold(chalk_1.yellow('[!]'))} No AWS credentials found, let's set them up! Or you can re-run the command and supply the AWS credentials in the same way when you invoke the AWS CLI.\n`);
|
|
42
42
|
yield prompt_1.requestAWSCredentials();
|
|
43
43
|
}
|
|
44
44
|
}
|
|
@@ -56,6 +56,12 @@ test('all option flags are supported', () => __awaiter(void 0, void 0, void 0, f
|
|
|
56
56
|
const usage = cli.usage(command_1.RunTestCommand);
|
|
57
57
|
options.forEach((option) => expect(usage).toContain(`--${option}`));
|
|
58
58
|
}));
|
|
59
|
+
const getAxiosHttpError = (status, error) => {
|
|
60
|
+
const serverError = new Error(error);
|
|
61
|
+
serverError.response = { data: { errors: [error] }, status };
|
|
62
|
+
serverError.config = { baseURL: 'baseURL', url: 'url' };
|
|
63
|
+
return serverError;
|
|
64
|
+
};
|
|
59
65
|
describe('run-test', () => {
|
|
60
66
|
beforeEach(() => {
|
|
61
67
|
jest.restoreAllMocks();
|
|
@@ -205,11 +211,8 @@ describe('run-test', () => {
|
|
|
205
211
|
jest.spyOn(ciUtils, 'parseConfigFile').mockImplementation((config, _) => __awaiter(void 0, void 0, void 0, function* () { return config; }));
|
|
206
212
|
jest.spyOn(utils, 'getSuites').mockImplementation((() => [conf]));
|
|
207
213
|
// Throw to stop the test
|
|
208
|
-
const serverError = new Error('Server Error');
|
|
209
|
-
serverError.response = { data: { errors: ['Bad Gateway'] }, status: 502 };
|
|
210
|
-
serverError.config = { baseURL: 'baseURL', url: 'url' };
|
|
211
214
|
const triggerTests = jest.fn(() => {
|
|
212
|
-
throw
|
|
215
|
+
throw getAxiosHttpError(502, 'Bad Gateway');
|
|
213
216
|
});
|
|
214
217
|
const apiHelper = {
|
|
215
218
|
getTest: jest.fn(() => (Object.assign({}, fixtures_1.getApiTest('publicId')))),
|
|
@@ -252,4 +255,104 @@ describe('run-test', () => {
|
|
|
252
255
|
}));
|
|
253
256
|
}));
|
|
254
257
|
});
|
|
258
|
+
describe('exit code respects `failOnCriticalErrors`', () => {
|
|
259
|
+
test('404 leading to `NO_TESTS_TO_RUN` never exit with 1', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
260
|
+
const command = new command_1.RunTestCommand();
|
|
261
|
+
command.context = { stdout: { write: jest.fn() } };
|
|
262
|
+
command['config'].failOnCriticalErrors = true;
|
|
263
|
+
const apiHelper = {
|
|
264
|
+
getTest: jest.fn(() => {
|
|
265
|
+
throw getAxiosHttpError(404, 'Test not found');
|
|
266
|
+
}),
|
|
267
|
+
};
|
|
268
|
+
jest.spyOn(runTests, 'getApiHelper').mockImplementation(() => apiHelper);
|
|
269
|
+
jest.spyOn(ciUtils, 'parseConfigFile').mockImplementation((config, _) => __awaiter(void 0, void 0, void 0, function* () { return config; }));
|
|
270
|
+
jest.spyOn(utils, 'getSuites').mockImplementation((() => [fixtures_1.getTestSuite()]));
|
|
271
|
+
expect(yield command.execute()).toBe(0);
|
|
272
|
+
expect(apiHelper.getTest).toHaveBeenCalledTimes(1);
|
|
273
|
+
}));
|
|
274
|
+
test('`NO_RESULTS_TO_POLL` never exit with 1', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
275
|
+
const command = new command_1.RunTestCommand();
|
|
276
|
+
command.context = { stdout: { write: jest.fn() } };
|
|
277
|
+
command['config'].failOnCriticalErrors = true;
|
|
278
|
+
const apiHelper = {
|
|
279
|
+
getTest: () => fixtures_1.getApiTest('123-456-789'),
|
|
280
|
+
triggerTests: jest.fn(() => ({})),
|
|
281
|
+
};
|
|
282
|
+
jest.spyOn(runTests, 'getApiHelper').mockImplementation(() => apiHelper);
|
|
283
|
+
jest.spyOn(ciUtils, 'parseConfigFile').mockImplementation((config, _) => __awaiter(void 0, void 0, void 0, function* () { return config; }));
|
|
284
|
+
jest.spyOn(utils, 'getSuites').mockImplementation((() => [fixtures_1.getTestSuite()]));
|
|
285
|
+
expect(yield command.execute()).toBe(0);
|
|
286
|
+
expect(apiHelper.triggerTests).toHaveBeenCalledTimes(1);
|
|
287
|
+
}));
|
|
288
|
+
describe.each([false, true])('%s', (failOnCriticalErrors) => {
|
|
289
|
+
const cases = [['HTTP 4xx error', 403], ['HTTP 5xx error', 502], ['Unknown error']];
|
|
290
|
+
const expectedExit = failOnCriticalErrors ? 1 : 0;
|
|
291
|
+
describe.each(cases)('%s', (_, errorCode) => {
|
|
292
|
+
test('unable to obtain test configurations', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
293
|
+
const command = new command_1.RunTestCommand();
|
|
294
|
+
command.context = { stdout: { write: jest.fn() } };
|
|
295
|
+
command['config'].failOnCriticalErrors = failOnCriticalErrors;
|
|
296
|
+
command['testSearchQuery'] = 'test:search';
|
|
297
|
+
const apiHelper = {
|
|
298
|
+
searchTests: jest.fn(() => {
|
|
299
|
+
throw errorCode ? getAxiosHttpError(errorCode, 'Error') : new Error('Unknown error');
|
|
300
|
+
}),
|
|
301
|
+
};
|
|
302
|
+
jest.spyOn(runTests, 'getApiHelper').mockImplementation(() => apiHelper);
|
|
303
|
+
jest.spyOn(ciUtils, 'parseConfigFile').mockImplementation((config, __) => __awaiter(void 0, void 0, void 0, function* () { return config; }));
|
|
304
|
+
expect(yield command.execute()).toBe(expectedExit);
|
|
305
|
+
expect(apiHelper.searchTests).toHaveBeenCalledTimes(1);
|
|
306
|
+
}));
|
|
307
|
+
test('unavailable test config', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
308
|
+
const command = new command_1.RunTestCommand();
|
|
309
|
+
command.context = { stdout: { write: jest.fn() } };
|
|
310
|
+
command['config'].failOnCriticalErrors = failOnCriticalErrors;
|
|
311
|
+
const apiHelper = {
|
|
312
|
+
getTest: jest.fn(() => {
|
|
313
|
+
throw errorCode ? getAxiosHttpError(errorCode, 'Error') : new Error('Unknown error');
|
|
314
|
+
}),
|
|
315
|
+
};
|
|
316
|
+
jest.spyOn(runTests, 'getApiHelper').mockImplementation(() => apiHelper);
|
|
317
|
+
jest.spyOn(ciUtils, 'parseConfigFile').mockImplementation((config, __) => __awaiter(void 0, void 0, void 0, function* () { return config; }));
|
|
318
|
+
jest.spyOn(utils, 'getSuites').mockImplementation((() => [fixtures_1.getTestSuite()]));
|
|
319
|
+
expect(yield command.execute()).toBe(expectedExit);
|
|
320
|
+
expect(apiHelper.getTest).toHaveBeenCalledTimes(1);
|
|
321
|
+
}));
|
|
322
|
+
test('unable to trigger tests', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
323
|
+
const command = new command_1.RunTestCommand();
|
|
324
|
+
command.context = { stdout: { write: jest.fn() } };
|
|
325
|
+
command['config'].failOnCriticalErrors = failOnCriticalErrors;
|
|
326
|
+
const apiHelper = {
|
|
327
|
+
getTest: () => fixtures_1.getApiTest('123-456-789'),
|
|
328
|
+
triggerTests: jest.fn(() => {
|
|
329
|
+
throw errorCode ? getAxiosHttpError(errorCode, 'Error') : new Error('Unknown error');
|
|
330
|
+
}),
|
|
331
|
+
};
|
|
332
|
+
jest.spyOn(runTests, 'getApiHelper').mockImplementation(() => apiHelper);
|
|
333
|
+
jest.spyOn(ciUtils, 'parseConfigFile').mockImplementation((config, __) => __awaiter(void 0, void 0, void 0, function* () { return config; }));
|
|
334
|
+
jest.spyOn(utils, 'getSuites').mockImplementation((() => [fixtures_1.getTestSuite()]));
|
|
335
|
+
expect(yield command.execute()).toBe(expectedExit);
|
|
336
|
+
expect(apiHelper.triggerTests).toHaveBeenCalledTimes(1);
|
|
337
|
+
}));
|
|
338
|
+
test('unable to poll test results', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
339
|
+
const command = new command_1.RunTestCommand();
|
|
340
|
+
command.context = { stdout: { write: jest.fn() } };
|
|
341
|
+
command['config'].failOnCriticalErrors = failOnCriticalErrors;
|
|
342
|
+
const apiHelper = {
|
|
343
|
+
getTest: () => fixtures_1.getApiTest('123-456-789'),
|
|
344
|
+
pollResults: jest.fn(() => {
|
|
345
|
+
throw errorCode ? getAxiosHttpError(errorCode, 'Error') : new Error('Unknown error');
|
|
346
|
+
}),
|
|
347
|
+
triggerTests: () => (Object.assign(Object.assign({}, fixtures_1.mockTestTriggerResponse), { results: [{ location: 1, public_id: '123-456-789', result_id: '1' }] })),
|
|
348
|
+
};
|
|
349
|
+
jest.spyOn(runTests, 'getApiHelper').mockImplementation(() => apiHelper);
|
|
350
|
+
jest.spyOn(ciUtils, 'parseConfigFile').mockImplementation((config, __) => __awaiter(void 0, void 0, void 0, function* () { return config; }));
|
|
351
|
+
jest.spyOn(utils, 'getSuites').mockImplementation((() => [fixtures_1.getTestSuite()]));
|
|
352
|
+
expect(yield command.execute()).toBe(expectedExit);
|
|
353
|
+
expect(apiHelper.pollResults).toHaveBeenCalledTimes(1);
|
|
354
|
+
}));
|
|
355
|
+
});
|
|
356
|
+
});
|
|
357
|
+
});
|
|
255
358
|
});
|
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
/// <reference types="node" />
|
|
3
3
|
import * as http from 'http';
|
|
4
4
|
import { ProxyConfiguration } from '../../../helpers/utils';
|
|
5
|
-
import { ApiTestResult, BrowserTestResult, CommandConfig, MainReporter, MultiStep, MultiStepsTestResult, PollResult, Step, Test } from '../interfaces';
|
|
5
|
+
import { ApiTestResult, BrowserTestResult, CommandConfig, MainReporter, MultiStep, MultiStepsTestResult, PollResult, Step, Suite, Test } from '../interfaces';
|
|
6
6
|
export declare const mockReporter: MainReporter;
|
|
7
7
|
export declare const ciConfig: CommandConfig;
|
|
8
8
|
export declare const getApiTest: (publicId: string) => Test;
|
|
9
9
|
export declare const getStep: () => Step;
|
|
10
10
|
export declare const getMultiStep: () => MultiStep;
|
|
11
|
+
export declare const getTestSuite: () => Suite;
|
|
11
12
|
export declare const getBrowserPollResult: () => PollResult;
|
|
12
13
|
export declare const getApiPollResult: () => PollResult;
|
|
13
14
|
export declare const getBrowserResult: (opts?: any) => BrowserTestResult;
|
|
@@ -19,7 +19,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
19
19
|
return result;
|
|
20
20
|
};
|
|
21
21
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
-
exports.getSyntheticsProxy = exports.mockPollResultResponse = exports.mockTestTriggerResponse = exports.mockSearchResponse = exports.getMultiStepsResult = exports.getApiResult = exports.getBrowserResult = exports.getApiPollResult = exports.getBrowserPollResult = exports.getMultiStep = exports.getStep = exports.getApiTest = exports.ciConfig = exports.mockReporter = void 0;
|
|
22
|
+
exports.getSyntheticsProxy = exports.mockPollResultResponse = exports.mockTestTriggerResponse = exports.mockSearchResponse = exports.getMultiStepsResult = exports.getApiResult = exports.getBrowserResult = exports.getApiPollResult = exports.getBrowserPollResult = exports.getTestSuite = exports.getMultiStep = exports.getStep = exports.getApiTest = exports.ciConfig = exports.mockReporter = void 0;
|
|
23
23
|
const http = __importStar(require("http"));
|
|
24
24
|
const url_1 = require("url");
|
|
25
25
|
const ws_1 = require("ws");
|
|
@@ -38,6 +38,7 @@ exports.mockReporter = {
|
|
|
38
38
|
testEnd: jest.fn(),
|
|
39
39
|
testTrigger: jest.fn(),
|
|
40
40
|
testWait: jest.fn(),
|
|
41
|
+
testsWait: jest.fn(),
|
|
41
42
|
};
|
|
42
43
|
exports.ciConfig = {
|
|
43
44
|
apiKey: '',
|
|
@@ -123,6 +124,8 @@ const getMultiStep = () => ({
|
|
|
123
124
|
},
|
|
124
125
|
});
|
|
125
126
|
exports.getMultiStep = getMultiStep;
|
|
127
|
+
const getTestSuite = () => ({ content: { tests: [{ config: {}, id: '123-456-789' }] }, name: 'Suite 1' });
|
|
128
|
+
exports.getTestSuite = getTestSuite;
|
|
126
129
|
const getPollResult = () => ({
|
|
127
130
|
dc_id: 1,
|
|
128
131
|
resultID: '123',
|
|
@@ -13,6 +13,7 @@ describe('Default reporter', () => {
|
|
|
13
13
|
};
|
|
14
14
|
const reporter = new default_1.DefaultReporter(mockContext);
|
|
15
15
|
it('should log for each hook', () => {
|
|
16
|
+
// `testWait` is skipped as nothing is logged for the default reporter.
|
|
16
17
|
const calls = [
|
|
17
18
|
['error', ['error']],
|
|
18
19
|
['initErrors', [['error']]],
|
|
@@ -21,7 +22,7 @@ describe('Default reporter', () => {
|
|
|
21
22
|
['runEnd', [utils_1.createSummary()]],
|
|
22
23
|
['testEnd', [{ options: {} }, [], '', []]],
|
|
23
24
|
['testTrigger', [{}, '', '', {}]],
|
|
24
|
-
['
|
|
25
|
+
['testsWait', [[{}]]],
|
|
25
26
|
];
|
|
26
27
|
for (const [fnName, args] of calls) {
|
|
27
28
|
reporter[fnName](...args);
|
|
@@ -254,10 +254,10 @@ describe('run-test', () => {
|
|
|
254
254
|
});
|
|
255
255
|
test('should throw an error if API or Application key are undefined', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
256
256
|
process.env = {};
|
|
257
|
-
expect(() => runTests.getApiHelper(fixtures_1.ciConfig)).toThrow(new errors_1.
|
|
258
|
-
yield expect(runTests.executeTests(fixtures_1.mockReporter, fixtures_1.ciConfig)).rejects.toMatchError(new errors_1.
|
|
259
|
-
expect(() => runTests.getApiHelper(Object.assign(Object.assign({}, fixtures_1.ciConfig), { appKey: 'fakeappkey' }))).toThrow(new errors_1.
|
|
260
|
-
yield expect(runTests.executeTests(fixtures_1.mockReporter, Object.assign(Object.assign({}, fixtures_1.ciConfig), { appKey: 'fakeappkey' }))).rejects.toMatchError(new errors_1.
|
|
257
|
+
expect(() => runTests.getApiHelper(fixtures_1.ciConfig)).toThrow(new errors_1.CriticalError('MISSING_APP_KEY'));
|
|
258
|
+
yield expect(runTests.executeTests(fixtures_1.mockReporter, fixtures_1.ciConfig)).rejects.toMatchError(new errors_1.CriticalError('MISSING_APP_KEY'));
|
|
259
|
+
expect(() => runTests.getApiHelper(Object.assign(Object.assign({}, fixtures_1.ciConfig), { appKey: 'fakeappkey' }))).toThrow(new errors_1.CriticalError('MISSING_API_KEY'));
|
|
260
|
+
yield expect(runTests.executeTests(fixtures_1.mockReporter, Object.assign(Object.assign({}, fixtures_1.ciConfig), { appKey: 'fakeappkey' }))).rejects.toMatchError(new errors_1.CriticalError('MISSING_API_KEY'));
|
|
261
261
|
}));
|
|
262
262
|
});
|
|
263
263
|
describe('getTestsList', () => {
|
|
@@ -38,6 +38,7 @@ const axios_1 = __importDefault(require("axios"));
|
|
|
38
38
|
const glob_1 = __importDefault(require("glob"));
|
|
39
39
|
const ciHelpers = __importStar(require("../../../helpers/ci"));
|
|
40
40
|
const api_1 = require("../api");
|
|
41
|
+
const errors_1 = require("../errors");
|
|
41
42
|
const interfaces_1 = require("../interfaces");
|
|
42
43
|
const utils = __importStar(require("../utils"));
|
|
43
44
|
const fixtures_1 = require("./fixtures");
|
|
@@ -163,6 +164,9 @@ describe('utils', () => {
|
|
|
163
164
|
if (fakeTests[publicId]) {
|
|
164
165
|
return { data: fakeTests[publicId] };
|
|
165
166
|
}
|
|
167
|
+
const error = new Error('Not found');
|
|
168
|
+
error.status = 404;
|
|
169
|
+
throw error;
|
|
166
170
|
}));
|
|
167
171
|
});
|
|
168
172
|
afterAll(() => {
|
|
@@ -191,7 +195,7 @@ describe('utils', () => {
|
|
|
191
195
|
expect(summary).toEqual(expectedSummary);
|
|
192
196
|
}));
|
|
193
197
|
test('no tests triggered throws an error', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
194
|
-
yield expect(utils.getTestsToTrigger(api, [], fixtures_1.mockReporter)).rejects.toEqual(new
|
|
198
|
+
yield expect(utils.getTestsToTrigger(api, [], fixtures_1.mockReporter)).rejects.toEqual(new errors_1.CiError('NO_TESTS_TO_RUN'));
|
|
195
199
|
}));
|
|
196
200
|
});
|
|
197
201
|
describe('handleConfig', () => {
|
|
@@ -9,6 +9,7 @@ export declare class EndpointError extends Error {
|
|
|
9
9
|
constructor(message: string, status: number);
|
|
10
10
|
}
|
|
11
11
|
export declare const formatBackendErrors: (requestError: AxiosError<BackendError>) => string;
|
|
12
|
+
export declare const isNotFoundError: (error: AxiosError | EndpointError) => boolean;
|
|
12
13
|
export declare const is5xxError: (error: AxiosError | EndpointError) => boolean | 0 | undefined;
|
|
13
14
|
export declare const apiConstructor: (configuration: APIConfiguration) => {
|
|
14
15
|
getPresignedURL: (testIds: string[]) => Promise<{
|
|
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.apiConstructor = exports.is5xxError = exports.formatBackendErrors = exports.EndpointError = void 0;
|
|
12
|
+
exports.apiConstructor = exports.is5xxError = exports.isNotFoundError = exports.formatBackendErrors = exports.EndpointError = void 0;
|
|
13
13
|
const querystring_1 = require("querystring");
|
|
14
14
|
const utils_1 = require("../../helpers/utils");
|
|
15
15
|
const utils_2 = require("./utils");
|
|
@@ -87,9 +87,11 @@ const retryOn5xxErrors = (retries, error) => {
|
|
|
87
87
|
return 500;
|
|
88
88
|
}
|
|
89
89
|
};
|
|
90
|
+
const getErrorHttpStatus = (error) => { var _a; return 'status' in error ? error.status : (_a = error.response) === null || _a === void 0 ? void 0 : _a.status; };
|
|
91
|
+
const isNotFoundError = (error) => getErrorHttpStatus(error) === 404;
|
|
92
|
+
exports.isNotFoundError = isNotFoundError;
|
|
90
93
|
const is5xxError = (error) => {
|
|
91
|
-
|
|
92
|
-
const statusCode = 'status' in error ? error.status : (_a = error.response) === null || _a === void 0 ? void 0 : _a.status;
|
|
94
|
+
const statusCode = getErrorHttpStatus(error);
|
|
93
95
|
return statusCode && statusCode >= 500 && statusCode <= 599;
|
|
94
96
|
};
|
|
95
97
|
exports.is5xxError = is5xxError;
|
|
@@ -68,8 +68,14 @@ class RunTestCommand extends clipanion_1.Command {
|
|
|
68
68
|
catch (error) {
|
|
69
69
|
if (error instanceof errors_1.CiError) {
|
|
70
70
|
this.reportCiError(error, this.reporter);
|
|
71
|
-
if (error instanceof errors_1.CriticalError
|
|
72
|
-
|
|
71
|
+
if (error instanceof errors_1.CriticalError) {
|
|
72
|
+
if (this.config.failOnCriticalErrors) {
|
|
73
|
+
return 1;
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
this.reporter.error(chalk_1.default.yellow('Because `failOnCriticalErrors` is not set or disabled, the command will exit with an error code 0. ' +
|
|
77
|
+
'Use `failOnCriticalErrors: true` to exit with an error code 1.\n'));
|
|
78
|
+
}
|
|
73
79
|
}
|
|
74
80
|
}
|
|
75
81
|
return 0;
|
|
@@ -128,12 +134,14 @@ class RunTestCommand extends clipanion_1.Command {
|
|
|
128
134
|
}
|
|
129
135
|
reportCiError(error, reporter) {
|
|
130
136
|
switch (error.code) {
|
|
137
|
+
// Non critical errors
|
|
131
138
|
case 'NO_RESULTS_TO_POLL':
|
|
132
139
|
reporter.log('No results to poll.\n');
|
|
133
140
|
break;
|
|
134
141
|
case 'NO_TESTS_TO_RUN':
|
|
135
142
|
reporter.log('No test to run.\n');
|
|
136
143
|
break;
|
|
144
|
+
// Critical command errors
|
|
137
145
|
case 'MISSING_APP_KEY':
|
|
138
146
|
reporter.error(`Missing ${chalk_1.default.red.bold('DATADOG_APP_KEY')} in your environment.\n`);
|
|
139
147
|
break;
|
|
@@ -144,16 +152,16 @@ class RunTestCommand extends clipanion_1.Command {
|
|
|
144
152
|
reporter.error(`\n${chalk_1.default.bgRed.bold(' ERROR: unable to poll test results ')}\n${error.message}\n\n`);
|
|
145
153
|
break;
|
|
146
154
|
case 'TUNNEL_START_FAILED':
|
|
147
|
-
reporter.error(`\n${chalk_1.default.bgRed.bold(' ERROR: unable to start tunnel')}\n${error.message}\n\n`);
|
|
155
|
+
reporter.error(`\n${chalk_1.default.bgRed.bold(' ERROR: unable to start tunnel ')}\n${error.message}\n\n`);
|
|
148
156
|
break;
|
|
149
157
|
case 'TRIGGER_TESTS_FAILED':
|
|
150
|
-
reporter.error(`\n${chalk_1.default.bgRed.bold(' ERROR: unable to trigger tests')}\n${error.message}\n\n`);
|
|
158
|
+
reporter.error(`\n${chalk_1.default.bgRed.bold(' ERROR: unable to trigger tests ')}\n${error.message}\n\n`);
|
|
151
159
|
break;
|
|
152
160
|
case 'UNAVAILABLE_TEST_CONFIG':
|
|
153
161
|
reporter.error(`\n${chalk_1.default.bgRed.bold(' ERROR: unable to obtain test configurations with search query ')}\n${error.message}\n\n`);
|
|
154
162
|
break;
|
|
155
163
|
case 'UNAVAILABLE_TUNNEL_CONFIG':
|
|
156
|
-
reporter.error(`\n${chalk_1.default.bgRed.bold(' ERROR: unable to get tunnel configuration')}\n${error.message}\n\n`);
|
|
164
|
+
reporter.error(`\n${chalk_1.default.bgRed.bold(' ERROR: unable to get tunnel configuration ')}\n${error.message}\n\n`);
|
|
157
165
|
}
|
|
158
166
|
}
|
|
159
167
|
resolveConfig() {
|
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
declare const
|
|
2
|
-
declare type
|
|
1
|
+
declare const nonCriticalErrorCodes: readonly ["NO_TESTS_TO_RUN", "NO_RESULTS_TO_POLL"];
|
|
2
|
+
declare type NonCriticalCiErrorCode = typeof nonCriticalErrorCodes[number];
|
|
3
|
+
declare const criticalErrorCodes: readonly ["UNAVAILABLE_TEST_CONFIG", "MISSING_API_KEY", "MISSING_APP_KEY", "UNAVAILABLE_TUNNEL_CONFIG", "TUNNEL_START_FAILED", "TRIGGER_TESTS_FAILED", "POLL_RESULTS_FAILED"];
|
|
4
|
+
declare type CriticalCiErrorCode = typeof criticalErrorCodes[number];
|
|
5
|
+
declare type CiErrorCode = NonCriticalCiErrorCode | CriticalCiErrorCode;
|
|
3
6
|
export declare class CiError extends Error {
|
|
4
7
|
code: CiErrorCode;
|
|
5
8
|
constructor(code: CiErrorCode);
|
|
6
9
|
}
|
|
7
10
|
export declare class CriticalError extends CiError {
|
|
11
|
+
code: CriticalCiErrorCode;
|
|
12
|
+
constructor(code: CriticalCiErrorCode);
|
|
8
13
|
}
|
|
9
14
|
export {};
|
|
@@ -2,12 +2,11 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.CriticalError = exports.CiError = void 0;
|
|
4
4
|
/* tslint:disable:max-classes-per-file */
|
|
5
|
-
const
|
|
5
|
+
const nonCriticalErrorCodes = ['NO_TESTS_TO_RUN', 'NO_RESULTS_TO_POLL'];
|
|
6
|
+
const criticalErrorCodes = [
|
|
6
7
|
'UNAVAILABLE_TEST_CONFIG',
|
|
7
8
|
'MISSING_API_KEY',
|
|
8
9
|
'MISSING_APP_KEY',
|
|
9
|
-
'NO_RESULTS_TO_POLL',
|
|
10
|
-
'NO_TESTS_TO_RUN',
|
|
11
10
|
'UNAVAILABLE_TUNNEL_CONFIG',
|
|
12
11
|
'TUNNEL_START_FAILED',
|
|
13
12
|
'TRIGGER_TESTS_FAILED',
|
|
@@ -21,5 +20,9 @@ class CiError extends Error {
|
|
|
21
20
|
}
|
|
22
21
|
exports.CiError = CiError;
|
|
23
22
|
class CriticalError extends CiError {
|
|
23
|
+
constructor(code) {
|
|
24
|
+
super(code);
|
|
25
|
+
this.code = code;
|
|
26
|
+
}
|
|
24
27
|
}
|
|
25
28
|
exports.CriticalError = CriticalError;
|
|
@@ -22,9 +22,10 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
22
22
|
return result;
|
|
23
23
|
};
|
|
24
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
-
exports.utils = exports.executeTests = exports.DefaultReporter = exports.CiError = void 0;
|
|
25
|
+
exports.utils = exports.executeTests = exports.DefaultReporter = exports.CriticalError = exports.CiError = void 0;
|
|
26
26
|
var errors_1 = require("./errors");
|
|
27
27
|
Object.defineProperty(exports, "CiError", { enumerable: true, get: function () { return errors_1.CiError; } });
|
|
28
|
+
Object.defineProperty(exports, "CriticalError", { enumerable: true, get: function () { return errors_1.CriticalError; } });
|
|
28
29
|
__exportStar(require("./interfaces"), exports);
|
|
29
30
|
var default_1 = require("./reporters/default");
|
|
30
31
|
Object.defineProperty(exports, "DefaultReporter", { enumerable: true, get: function () { return default_1.DefaultReporter; } });
|
|
@@ -11,6 +11,7 @@ export interface MainReporter {
|
|
|
11
11
|
}): void;
|
|
12
12
|
runEnd(summary: Summary): void;
|
|
13
13
|
testEnd(test: Test, results: PollResult[], baseUrl: string, locationNames: LocationsMapping, failOnCriticalErrors: boolean, failOnTimeout: boolean): void;
|
|
14
|
+
testsWait(tests: Test[]): void;
|
|
14
15
|
testTrigger(test: Test, testId: string, executionRule: ExecutionRule, config: ConfigOverride): void;
|
|
15
16
|
testWait(test: Test): void;
|
|
16
17
|
}
|
|
@@ -13,6 +13,7 @@ export declare class DefaultReporter implements MainReporter {
|
|
|
13
13
|
}): void;
|
|
14
14
|
runEnd(summary: Summary): void;
|
|
15
15
|
testEnd(test: Test, results: PollResult[], baseUrl: string, locationNames: LocationsMapping, failOnCriticalErrors: boolean, failOnTimeout: boolean): void;
|
|
16
|
+
testsWait(tests: Test[]): void;
|
|
16
17
|
testTrigger(test: Test, testId: string, executionRule: ExecutionRule, config: ConfigOverride): void;
|
|
17
18
|
testWait(test: Test): void;
|
|
18
19
|
}
|
|
@@ -195,14 +195,14 @@ class DefaultReporter {
|
|
|
195
195
|
this.write(error);
|
|
196
196
|
}
|
|
197
197
|
initErrors(errors) {
|
|
198
|
-
this.write(errors.join('\n'));
|
|
198
|
+
this.write(errors.join('\n') + '\n');
|
|
199
199
|
}
|
|
200
200
|
log(log) {
|
|
201
201
|
this.write(log);
|
|
202
202
|
}
|
|
203
203
|
reportStart(timings) {
|
|
204
204
|
const delay = (Date.now() - timings.startTime).toString();
|
|
205
|
-
this.write(['
|
|
205
|
+
this.write(['', chalk_1.default.bold.cyan('=== REPORT ==='), `Took ${chalk_1.default.bold(delay)}ms`, '\n'].join('\n'));
|
|
206
206
|
}
|
|
207
207
|
runEnd(summary) {
|
|
208
208
|
const summaries = [
|
|
@@ -239,6 +239,15 @@ class DefaultReporter {
|
|
|
239
239
|
.concat('\n\n');
|
|
240
240
|
this.write([`${icon} ${idDisplay}${nonBlockingText} | ${nameColor(test.name)}`, testResultsText].join('\n'));
|
|
241
241
|
}
|
|
242
|
+
testsWait(tests) {
|
|
243
|
+
const testsList = tests.map((t) => t.public_id);
|
|
244
|
+
if (testsList.length > 10) {
|
|
245
|
+
testsList.splice(10);
|
|
246
|
+
testsList.push('…');
|
|
247
|
+
}
|
|
248
|
+
const testsDisplay = chalk_1.default.gray(`(${testsList.join(', ')})`);
|
|
249
|
+
this.write(`\nWaiting for ${chalk_1.default.bold.cyan(tests.length)} test result${tests.length > 1 ? 's' : ''} ${testsDisplay}…\n`);
|
|
250
|
+
}
|
|
242
251
|
testTrigger(test, testId, executionRule, config) {
|
|
243
252
|
const idDisplay = `[${chalk_1.default.bold.dim(testId)}]`;
|
|
244
253
|
const getMessage = () => {
|
|
@@ -246,22 +255,21 @@ class DefaultReporter {
|
|
|
246
255
|
// Test is either skipped from datadog-ci config or from test config
|
|
247
256
|
const isSkippedByCIConfig = config.executionRule === interfaces_1.ExecutionRule.SKIPPED;
|
|
248
257
|
if (isSkippedByCIConfig) {
|
|
249
|
-
return
|
|
258
|
+
return `Skipped test "${chalk_1.default.yellow.dim(test.name)}"`;
|
|
250
259
|
}
|
|
251
260
|
else {
|
|
252
|
-
return
|
|
261
|
+
return `Skipped test "${chalk_1.default.yellow.dim(test.name)}" because of execution rule configuration in Datadog`;
|
|
253
262
|
}
|
|
254
263
|
}
|
|
255
264
|
if (executionRule === interfaces_1.ExecutionRule.NON_BLOCKING) {
|
|
256
|
-
return `
|
|
265
|
+
return `Found test "${chalk_1.default.green.bold(test.name)}" (non-blocking)`;
|
|
257
266
|
}
|
|
258
|
-
return `
|
|
267
|
+
return `Found test "${chalk_1.default.green.bold(test.name)}"`;
|
|
259
268
|
};
|
|
260
269
|
this.write(`${idDisplay} ${getMessage()}\n`);
|
|
261
270
|
}
|
|
262
271
|
testWait(test) {
|
|
263
|
-
|
|
264
|
-
this.write(`${idDisplay} Waiting results for "${chalk_1.default.green.bold(test.name)}"\n`);
|
|
272
|
+
return;
|
|
265
273
|
}
|
|
266
274
|
}
|
|
267
275
|
exports.DefaultReporter = DefaultReporter;
|
|
@@ -32,8 +32,7 @@ const executeTests = (reporter, config) => __awaiter(void 0, void 0, void 0, fun
|
|
|
32
32
|
testsToTrigger = yield exports.getTestsList(api, config, reporter);
|
|
33
33
|
}
|
|
34
34
|
catch (error) {
|
|
35
|
-
|
|
36
|
-
throw new (isCriticalError ? errors_1.CriticalError : errors_1.CiError)('UNAVAILABLE_TEST_CONFIG');
|
|
35
|
+
throw new errors_1.CriticalError('UNAVAILABLE_TEST_CONFIG');
|
|
37
36
|
}
|
|
38
37
|
}
|
|
39
38
|
if (!testsToTrigger.length) {
|
|
@@ -44,8 +43,7 @@ const executeTests = (reporter, config) => __awaiter(void 0, void 0, void 0, fun
|
|
|
44
43
|
testsToTriggerResult = yield utils_1.getTestsToTrigger(api, testsToTrigger, reporter);
|
|
45
44
|
}
|
|
46
45
|
catch (error) {
|
|
47
|
-
|
|
48
|
-
throw new (isCriticalError ? errors_1.CriticalError : errors_1.CiError)('UNAVAILABLE_TEST_CONFIG');
|
|
46
|
+
throw error instanceof errors_1.CiError ? error : new errors_1.CriticalError('UNAVAILABLE_TEST_CONFIG');
|
|
49
47
|
}
|
|
50
48
|
const { tests, overriddenTestsToTrigger, summary } = testsToTriggerResult;
|
|
51
49
|
// All tests have been skipped or are missing.
|
|
@@ -60,8 +58,7 @@ const executeTests = (reporter, config) => __awaiter(void 0, void 0, void 0, fun
|
|
|
60
58
|
presignedURL = (yield api.getPresignedURL(publicIdsToTrigger)).url;
|
|
61
59
|
}
|
|
62
60
|
catch (error) {
|
|
63
|
-
|
|
64
|
-
throw new (isCriticalError ? errors_1.CriticalError : errors_1.CiError)('UNAVAILABLE_TUNNEL_CONFIG');
|
|
61
|
+
throw new errors_1.CriticalError('UNAVAILABLE_TUNNEL_CONFIG');
|
|
65
62
|
}
|
|
66
63
|
// Open a tunnel to Datadog
|
|
67
64
|
try {
|
|
@@ -72,9 +69,8 @@ const executeTests = (reporter, config) => __awaiter(void 0, void 0, void 0, fun
|
|
|
72
69
|
});
|
|
73
70
|
}
|
|
74
71
|
catch (error) {
|
|
75
|
-
const isCriticalError = api_1.is5xxError(error);
|
|
76
72
|
yield stopTunnel();
|
|
77
|
-
throw new
|
|
73
|
+
throw new errors_1.CriticalError('TUNNEL_START_FAILED');
|
|
78
74
|
}
|
|
79
75
|
}
|
|
80
76
|
let triggers;
|
|
@@ -82,13 +78,12 @@ const executeTests = (reporter, config) => __awaiter(void 0, void 0, void 0, fun
|
|
|
82
78
|
triggers = yield utils_1.runTests(api, overriddenTestsToTrigger);
|
|
83
79
|
}
|
|
84
80
|
catch (error) {
|
|
85
|
-
const isCriticalError = api_1.is5xxError(error);
|
|
86
81
|
yield stopTunnel();
|
|
87
|
-
throw new
|
|
82
|
+
throw new errors_1.CriticalError('TRIGGER_TESTS_FAILED');
|
|
88
83
|
}
|
|
89
84
|
if (!triggers.results) {
|
|
90
85
|
yield stopTunnel();
|
|
91
|
-
throw new errors_1.
|
|
86
|
+
throw new errors_1.CiError('NO_RESULTS_TO_POLL');
|
|
92
87
|
}
|
|
93
88
|
const results = {};
|
|
94
89
|
try {
|
|
@@ -97,8 +92,7 @@ const executeTests = (reporter, config) => __awaiter(void 0, void 0, void 0, fun
|
|
|
97
92
|
Object.assign(results, resultPolled);
|
|
98
93
|
}
|
|
99
94
|
catch (error) {
|
|
100
|
-
|
|
101
|
-
throw new (isCriticalError ? errors_1.CriticalError : errors_1.CiError)('POLL_RESULTS_FAILED');
|
|
95
|
+
throw new errors_1.CriticalError('POLL_RESULTS_FAILED');
|
|
102
96
|
}
|
|
103
97
|
finally {
|
|
104
98
|
yield stopTunnel();
|
|
@@ -132,10 +126,10 @@ const getTestsList = (api, config, reporter) => __awaiter(void 0, void 0, void 0
|
|
|
132
126
|
exports.getTestsList = getTestsList;
|
|
133
127
|
const getApiHelper = (config) => {
|
|
134
128
|
if (!config.appKey) {
|
|
135
|
-
throw new errors_1.
|
|
129
|
+
throw new errors_1.CriticalError('MISSING_APP_KEY');
|
|
136
130
|
}
|
|
137
131
|
if (!config.apiKey) {
|
|
138
|
-
throw new errors_1.
|
|
132
|
+
throw new errors_1.CriticalError('MISSING_API_KEY');
|
|
139
133
|
}
|
|
140
134
|
return api_1.apiConstructor({
|
|
141
135
|
apiKey: config.apiKey,
|
|
@@ -41,6 +41,7 @@ const glob_1 = __importDefault(require("glob"));
|
|
|
41
41
|
const ci_1 = require("../../helpers/ci");
|
|
42
42
|
const utils_1 = require("../../helpers/utils");
|
|
43
43
|
const api_1 = require("./api");
|
|
44
|
+
const errors_1 = require("./errors");
|
|
44
45
|
const interfaces_1 = require("./interfaces");
|
|
45
46
|
const POLLING_INTERVAL = 5000; // In ms
|
|
46
47
|
const PUBLIC_ID_REGEX = /^[\d\w]{3}-[\d\w]{3}-[\d\w]{3}$/;
|
|
@@ -370,6 +371,13 @@ const getReporter = (reporters) => ({
|
|
|
370
371
|
}
|
|
371
372
|
}
|
|
372
373
|
},
|
|
374
|
+
testsWait: (tests) => {
|
|
375
|
+
for (const reporter of reporters) {
|
|
376
|
+
if (typeof reporter.testsWait === 'function') {
|
|
377
|
+
reporter.testsWait(tests);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
},
|
|
373
381
|
});
|
|
374
382
|
exports.getReporter = getReporter;
|
|
375
383
|
const getTestsToTrigger = (api, triggerConfigs, reporter) => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -382,14 +390,14 @@ const getTestsToTrigger = (api, triggerConfigs, reporter) => __awaiter(void 0, v
|
|
|
382
390
|
try {
|
|
383
391
|
test = Object.assign(Object.assign({}, (yield api.getTest(id))), { suite });
|
|
384
392
|
}
|
|
385
|
-
catch (
|
|
386
|
-
if (api_1.
|
|
387
|
-
|
|
393
|
+
catch (error) {
|
|
394
|
+
if (api_1.isNotFoundError(error)) {
|
|
395
|
+
summary.testsNotFound.add(id);
|
|
396
|
+
const errorMessage = api_1.formatBackendErrors(error);
|
|
397
|
+
errorMessages.push(`[${chalk_1.default.bold.dim(id)}] ${chalk_1.default.yellow.bold('Test not found')}: ${errorMessage}`);
|
|
398
|
+
return;
|
|
388
399
|
}
|
|
389
|
-
|
|
390
|
-
const errorMessage = api_1.formatBackendErrors(e);
|
|
391
|
-
errorMessages.push(`[${chalk_1.default.bold.dim(id)}] ${chalk_1.default.yellow.bold('Test not found')}: ${errorMessage}\n`);
|
|
392
|
-
return;
|
|
400
|
+
throw error;
|
|
393
401
|
}
|
|
394
402
|
const overriddenConfig = exports.handleConfig(test, id, reporter, config);
|
|
395
403
|
overriddenTestsToTrigger.push(overriddenConfig);
|
|
@@ -405,9 +413,13 @@ const getTestsToTrigger = (api, triggerConfigs, reporter) => __awaiter(void 0, v
|
|
|
405
413
|
// Display errors at the end of all tests for better visibility.
|
|
406
414
|
reporter.initErrors(errorMessages);
|
|
407
415
|
if (!overriddenTestsToTrigger.length) {
|
|
408
|
-
throw new
|
|
416
|
+
throw new errors_1.CiError('NO_TESTS_TO_RUN');
|
|
417
|
+
}
|
|
418
|
+
const waitedTests = tests.filter(definedTypeGuard);
|
|
419
|
+
if (waitedTests.length > 0) {
|
|
420
|
+
reporter.testsWait(waitedTests);
|
|
409
421
|
}
|
|
410
|
-
return { tests:
|
|
422
|
+
return { tests: waitedTests, overriddenTestsToTrigger, summary };
|
|
411
423
|
});
|
|
412
424
|
exports.getTestsToTrigger = getTestsToTrigger;
|
|
413
425
|
const runTests = (api, testsToTrigger) => __awaiter(void 0, void 0, void 0, function* () {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datadog/datadog-ci",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Run datadog actions from the CI.",
|
|
5
5
|
"repository": "https://github.com/DataDog/datadog-ci",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -23,11 +23,20 @@
|
|
|
23
23
|
"ini": "1.3.7",
|
|
24
24
|
"kind-of@^6.0.0": "6.0.3"
|
|
25
25
|
},
|
|
26
|
+
"pkg": {
|
|
27
|
+
"scripts": [
|
|
28
|
+
"dist/commands/*/*.js",
|
|
29
|
+
"node_modules/vm2/lib/contextify.js",
|
|
30
|
+
"node_modules/vm2/lib/setup-sandbox.js"
|
|
31
|
+
]
|
|
32
|
+
},
|
|
26
33
|
"scripts": {
|
|
27
34
|
"build": "yarn clean; tsc",
|
|
35
|
+
"build:win": "tsc",
|
|
28
36
|
"check-licenses": "node bin/check-licenses.js",
|
|
37
|
+
"clean:win": "rm dist -r",
|
|
29
38
|
"clean": "rm -rf dist/*",
|
|
30
|
-
"dist-standalone": "
|
|
39
|
+
"dist-standalone": "pkg --compress GZip .",
|
|
31
40
|
"dist-standalone:test": "jest --config ./jest.config-standalone.js",
|
|
32
41
|
"format": "yarn tslint --fix && yarn prettier --write",
|
|
33
42
|
"launch": "ts-node --transpile-only src/cli.ts",
|
|
@@ -84,7 +93,7 @@
|
|
|
84
93
|
"jest": "27.0.5",
|
|
85
94
|
"jest-environment-node": "27.0.5",
|
|
86
95
|
"jest-matcher-specific-error": "1.0.0",
|
|
87
|
-
"
|
|
96
|
+
"pkg": "5.5.2",
|
|
88
97
|
"prettier": "2.0.5",
|
|
89
98
|
"proxy": "1.0.2",
|
|
90
99
|
"ts-jest": "27.0.3",
|