@testrevolution/bugbug-cli 12.20.0 → 12.21.1

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@testrevolution/bugbug-cli",
3
3
  "description": "BugBug CLI",
4
- "version": "12.20.0",
4
+ "version": "12.21.1",
5
5
  "keywords": [
6
6
  "automation",
7
7
  "cli",
@@ -18,13 +18,14 @@
18
18
  "bugbug": "bin/bugbug"
19
19
  },
20
20
  "dependencies": {
21
- "@sentry/node": "^8.55.0",
21
+ "@sentry/node": "^10.5.0",
22
22
  "axios": "^1.10.0",
23
23
  "console-table-printer": "^2.12.1",
24
24
  "dotenv": "^16.4.7",
25
25
  "jest-junit": "^16.0.0",
26
26
  "junit-xml": "^1.2.0",
27
27
  "minimist": "^1.2.8",
28
+ "nanoid": "^3.3.4",
28
29
  "ora": "^5.4.1",
29
30
  "path": "^0.12.7",
30
31
  "validator": "^13.15.0"
@@ -0,0 +1,8 @@
1
+ module.exports = {
2
+ init: jest.fn(),
3
+ setTag: jest.fn(),
4
+ setTags: jest.fn(),
5
+ setContext: jest.fn(),
6
+ flush: jest.fn(),
7
+ addBreadcrumb: jest.fn(),
8
+ };
@@ -0,0 +1,3 @@
1
+ module.exports = {
2
+ nanoid: () => 12345,
3
+ };
@@ -5,7 +5,7 @@ const settings = require('../settings');
5
5
  const { getSpinner } = require('../utils/spinner');
6
6
  const { getConfigValue } = require('../utils/config');
7
7
  const {
8
- getExitCode, parseVariables, printErrorResponse, getUnknownOptions,
8
+ getExitCode, parseVariables, handleResponseError, getUnknownOptions,
9
9
  } = require('../utils/helper');
10
10
  const { apiCall, apiCallPoll } = require('../utils/api');
11
11
  const {
@@ -18,6 +18,7 @@ const {
18
18
  generateJunitReport,
19
19
  } = require('../utils/testReports');
20
20
  const help = require('./help');
21
+ const { sentry } = require('../utils/sentry');
21
22
 
22
23
  const OBJECT_TYPES = [settings.TYPE_TEST, settings.TYPE_SUITE, settings.TYPE_PROFILE];
23
24
 
@@ -34,7 +35,7 @@ const getList = async (type, query, extraParams) => {
34
35
  return getExitCode(true);
35
36
  }
36
37
  } catch (error) {
37
- await printErrorResponse(spinner, error);
38
+ await handleResponseError(spinner, error);
38
39
  }
39
40
  return getExitCode(false);
40
41
  };
@@ -53,6 +54,7 @@ const getResult = async (type, id, extraParams) => {
53
54
  if (result) {
54
55
  switch (reporter) {
55
56
  case settings.REPORTER_TYPE.junit: {
57
+ console.log('Generating JUnit report...');
56
58
  await generateJunitReport(type, result.data, outputPath);
57
59
  break;
58
60
  }
@@ -68,7 +70,7 @@ const getResult = async (type, id, extraParams) => {
68
70
  return getExitCode(result.data.status);
69
71
  }
70
72
  } catch (error) {
71
- await printErrorResponse(spinner, error);
73
+ await handleResponseError(spinner, error);
72
74
  }
73
75
 
74
76
  return getExitCode(false);
@@ -101,7 +103,7 @@ const checkStatus = async (type, id, extraParams) => {
101
103
  }
102
104
  exitCode = getExitCode(response.data.status);
103
105
  } catch (error) {
104
- await printErrorResponse(spinner, error);
106
+ await handleResponseError(spinner, error);
105
107
  await getResult(type, id, extraParams);
106
108
  exitCode = getExitCode(false);
107
109
  }
@@ -123,7 +125,7 @@ const run = async (type, data, extraParams) => {
123
125
  return getExitCode(true);
124
126
  }
125
127
  } catch (error) {
126
- await printErrorResponse(spinner, error);
128
+ await handleResponseError(spinner, error);
127
129
  }
128
130
  return getExitCode(false);
129
131
  };
@@ -149,7 +151,7 @@ const stop = async (type, { id }, extraParams) => {
149
151
  if (error.response?.status === 404) {
150
152
  spinner.fail(`No ${type}Run with id ${id} found.`);
151
153
  } else {
152
- await printErrorResponse(spinner, error);
154
+ await handleResponseError(spinner, error);
153
155
  }
154
156
  }
155
157
  return getExitCode(false);
@@ -178,6 +180,7 @@ const checkTokenConfig = async () => {
178
180
  }
179
181
  } catch (err) {
180
182
  console.error(err);
183
+ sentry.captureException(err);
181
184
  }
182
185
  };
183
186
 
@@ -242,6 +245,18 @@ module.exports = async (args) => {
242
245
  subCmd, objectType, id, variables, extraParams,
243
246
  } = await parseArgs(args);
244
247
 
248
+ sentry.setTags({
249
+ Command: 'remote',
250
+ 'Sub command': subCmd,
251
+ 'Command object type': objectType,
252
+ 'Triggered by': extraParams.triggeredBy,
253
+ Reporter: extraParams.reporter,
254
+ });
255
+ sentry.addBreadcrumb({
256
+ message: `Run "${subCmd}" remote command`,
257
+ category: 'Command',
258
+ });
259
+
245
260
  let exitCode;
246
261
  await checkTokenConfig();
247
262
  switch (subCmd) {
package/src/index.js CHANGED
@@ -1,18 +1,19 @@
1
1
  const path = require('path');
2
2
  const minimist = require('minimist');
3
- const api = require('./utils/api');
4
- const sentry = require('./utils/sentry');
5
- const commands = require('./commands');
3
+ const { nanoid } = require('nanoid');
4
+ const { sentry, init: initSentry } = require('./utils/sentry');
6
5
  const settings = require('./settings');
7
- const config = require('./utils/config');
8
- const helper = require('./utils/helper');
9
-
10
6
  require('dotenv').config();
11
7
  require('dotenv').config({
12
8
  path: path.resolve(process.cwd(), `.env.${settings.NODE_ENV}`),
13
9
  });
10
+ // Initialize Sentry first
11
+ initSentry();
14
12
 
15
- sentry.init();
13
+ const api = require('./utils/api');
14
+ const commands = require('./commands');
15
+ const config = require('./utils/config');
16
+ const helper = require('./utils/helper');
16
17
 
17
18
  module.exports = async () => {
18
19
  await config.initConfigDir();
@@ -21,7 +22,14 @@ module.exports = async () => {
21
22
  let exitCode;
22
23
 
23
24
  await helper.overrideSettings(args);
24
- api.mountAxiosInterceptors(`${args.debug}` === 'true');
25
+ const processId = nanoid(8);
26
+
27
+ api.mountAxiosInterceptors({
28
+ processId,
29
+ debug: `${args.debug}` === 'true',
30
+ });
31
+
32
+ sentry.setTag('ProcessId', processId);
25
33
 
26
34
  switch (cmd) {
27
35
  case 'config':
package/src/utils/api.js CHANGED
@@ -4,8 +4,13 @@ const config = require('./config');
4
4
  const print = require('./print');
5
5
  const { getSpinner } = require('./spinner');
6
6
 
7
- const interceptRequest = (isDebugEnabled) => (request) => {
8
- if (isDebugEnabled) {
7
+ const interceptRequest = ({ processId, debug }) => (request) => {
8
+ request.params = {
9
+ ...(request.params || {}),
10
+ reqId: processId,
11
+ };
12
+
13
+ if (debug) {
9
14
  print.printObject({
10
15
  url: request.url,
11
16
  headers: request.headers,
@@ -26,16 +31,16 @@ const printResponseData = (response) => {
26
31
  });
27
32
  };
28
33
 
29
- const interceptResponseSuccess = (isDebugEnabled = false) => (response) => {
30
- if (isDebugEnabled) {
34
+ const interceptResponseSuccess = ({ debug }) => (response) => {
35
+ if (debug) {
31
36
  printResponseData(response);
32
37
  }
33
38
  return response;
34
39
  };
35
40
 
36
- const interceptResponseError = (spinner, isDebugEnabled = false) => (error) => {
41
+ const interceptResponseError = (spinner, { debug }) => (error) => {
37
42
  const { response } = error;
38
- if (response && isDebugEnabled) {
43
+ if (response && debug) {
39
44
  printResponseData(response);
40
45
  }
41
46
  if (response && response.status === 400) {
@@ -66,13 +71,13 @@ const interceptResponseError = (spinner, isDebugEnabled = false) => (error) => {
66
71
  return Promise.reject(error);
67
72
  };
68
73
 
69
- const mountAxiosInterceptors = (printRawResponse) => {
74
+ const mountAxiosInterceptors = ({ processId, debug }) => {
70
75
  const spinner = getSpinner(false);
71
76
  axios.defaults.headers.common['Content-Type'] = 'application/json';
72
- axios.interceptors.request.use(interceptRequest(printRawResponse));
77
+ axios.interceptors.request.use(interceptRequest({ processId, debug }));
73
78
  axios.interceptors.response.use(
74
- interceptResponseSuccess(printRawResponse),
75
- interceptResponseError(spinner, printRawResponse),
79
+ interceptResponseSuccess({ debug }),
80
+ interceptResponseError(spinner, { debug }),
76
81
  );
77
82
  };
78
83
 
@@ -1,7 +1,14 @@
1
+ const { nanoid } = require('nanoid');
1
2
  const settings = require('../settings');
3
+ const { sentry } = require('./sentry');
2
4
 
3
5
  const getExitCode = async (status) => {
4
6
  const exitCode = settings.EXIT_CODES[status];
7
+
8
+ // We need to flush the sentry buffer
9
+ // to ensure the error is sent to Sentry before the process exits
10
+ await sentry.flush(2000);
11
+
5
12
  return exitCode === undefined ? 1 : exitCode;
6
13
  };
7
14
 
@@ -25,14 +32,17 @@ const getUnknownOptions = async (args, knownKeys) => {
25
32
  return unknownKeys;
26
33
  };
27
34
 
28
- const printErrorResponse = async (spinner, error) => {
29
- if (
30
- error.response?.status === 400
31
- && error.response?.data[0]?.message) {
32
- spinner.fail(error.response.data[0].message);
33
- } else {
34
- spinner.fail(error.toString());
35
+ const handleResponseError = async (spinner, error) => {
36
+ const returnedMessage = error.response?.data[0]?.message;
37
+ const errorId = error.request?.params?.reqId ?? nanoid(8);
38
+
39
+ if (returnedMessage) {
40
+ spinner.fail(returnedMessage);
41
+ return;
35
42
  }
43
+
44
+ sentry.captureException(error);
45
+ spinner.fail(`Unexpected error occurred (id: ${errorId})`);
36
46
  };
37
47
 
38
48
  const overrideSettings = async (args) => {
@@ -76,5 +86,5 @@ module.exports = {
76
86
  overrideSettings,
77
87
  parseTimeFromDuration,
78
88
  parseVariables,
79
- printErrorResponse,
89
+ handleResponseError,
80
90
  };
@@ -7,9 +7,11 @@ const init = () => {
7
7
  environment: process.env.ENV,
8
8
  release: `${pkgJson.version}-${process.env.ENV}`,
9
9
  normalizeDepth: 8,
10
+ sendDefaultPii: true,
10
11
  });
11
12
  };
12
13
 
13
14
  module.exports = {
14
15
  init,
16
+ sentry: Sentry,
15
17
  };
@@ -6,18 +6,20 @@ const settings = require('../settings');
6
6
  const { getSecondsFromDuration } = require('./helper');
7
7
 
8
8
  const getTestInJunitFormat = (testRun) => {
9
- const errorDetails = testRun.errorCode ? {
10
- message: `Error ${testRun.errorCode} occurred in step: ${testRun.details[0].step.type} (${testRun.details[0].step.id})`,
11
- stack: testRun.errorCode,
12
- } : undefined;
9
+ const errorCode = testRun.errorCode ?? 'MISSING_ERROR_CODE';
10
+ const errorDetails = {
11
+ message: `Error ${errorCode} occurred in step: ${testRun.details[0].step.type} (${testRun.details[0].step.id})`,
12
+ stack: errorCode,
13
+ };
14
+ const errorsList = errorDetails ? [errorDetails] : [];
13
15
 
14
16
  return {
15
17
  id: testRun.id,
16
18
  name: testRun.name,
17
19
  time: getSecondsFromDuration(testRun.duration),
18
20
  classname: null,
19
- errors: testRun.status === settings.STATUS_ERROR ? [errorDetails] : [],
20
- failures: testRun.status === settings.STATUS_FAILED ? [errorDetails] : [],
21
+ errors: testRun.status === settings.STATUS_ERROR ? errorsList : [],
22
+ failures: testRun.status === settings.STATUS_FAILED ? errorsList : [],
21
23
  };
22
24
  };
23
25