@teamscale/coverage-collector 0.1.0-beta.4 → 0.1.0-beta.6

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/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teamscale/coverage-collector",
3
- "version": "0.1.0-beta.4",
3
+ "version": "0.1.0-beta.6",
4
4
  "description": "Collector for JavaScript code coverage information",
5
5
  "main": "dist/src/main.js",
6
6
  "bin": "dist/src/main.js",
@@ -22,13 +22,13 @@
22
22
  "dist/**/*"
23
23
  ],
24
24
  "dependencies": {
25
- "@cqse/commons": "0.1.0-beta.2",
25
+ "@cqse/commons": "0.1.0-beta.5",
26
26
  "argparse": "^2.0.1",
27
27
  "async": "^3.2.5",
28
- "axios": "^1.6.5",
28
+ "axios": "^1.6.7",
29
29
  "bunyan": "^1.8.15",
30
- "date-and-time": "^3.1.0",
31
- "dotenv": "^16.3.1",
30
+ "date-and-time": "^3.1.1",
31
+ "dotenv": "^16.4.5",
32
32
  "express": "^4.18.2",
33
33
  "form-data": "^4.0.0",
34
34
  "mkdirp": "^3.0.1",
@@ -39,22 +39,22 @@
39
39
  "ws": "^8.16.0"
40
40
  },
41
41
  "devDependencies": {
42
- "@babel/core": "^7.23.7",
43
- "@babel/preset-env": "^7.23.8",
42
+ "@babel/core": "^7.23.9",
43
+ "@babel/preset-env": "^7.23.9",
44
44
  "@types/argparse": "^2.0.14",
45
45
  "@types/async": "^3.2.24",
46
46
  "@types/bunyan": "^1.8.11",
47
47
  "@types/express": "^4.17.21",
48
- "@types/jest": "^29.5.11",
49
- "@types/node": "^20.11.0",
48
+ "@types/jest": "^29.5.12",
49
+ "@types/node": "^20.11.19",
50
50
  "@types/tmp": "^0.2.6",
51
51
  "@types/ws": "^8.5.10",
52
52
  "babel-jest": "^29.7.0",
53
- "esbuild": "^0.19.11",
53
+ "esbuild": "^0.20.1",
54
54
  "jest": "^29.7.0",
55
55
  "mockttp": "^3.10.1",
56
56
  "rimraf": "^5.0.5",
57
- "ts-jest": "^29.1.1",
57
+ "ts-jest": "^29.1.2",
58
58
  "ts-node": "^10.9.2",
59
59
  "typescript": "^5.3.3"
60
60
  },
package/dist/src/App.d.ts CHANGED
@@ -25,6 +25,11 @@ export declare class App {
25
25
  static runWithConfig(config: ConfigParameters): {
26
26
  stop: () => Promise<void>;
27
27
  };
28
+ /**
29
+ * Starts a timer that shows a message every min that no coverage
30
+ * was received until the opposite is the case.
31
+ */
32
+ private static startNoMessageTimer;
28
33
  /**
29
34
  * Start a timer for dumping the data, depending on the configuration.
30
35
  *
package/dist/src/App.js CHANGED
@@ -101,8 +101,10 @@ class App {
101
101
  // Start the server socket.
102
102
  // ATTENTION: The server is executed asynchronously
103
103
  const serverState = server.start();
104
- // Optionally, start a timer that dumps the coverage after a N seconds
105
- const timerState = this.maybeStartDumpTimer(config, storage, logger);
104
+ // Optionally, start a timer that dumps the coverage after N seconds
105
+ const dumpTimerState = this.maybeStartDumpTimer(config, storage, logger);
106
+ // Start a timer that informs if no coverage was received within the last minute
107
+ const statsTimerState = this.startNoMessageTimer(logger, server);
106
108
  // Say bye bye on CTRL+C and exit the process
107
109
  process.on('SIGINT', async () => {
108
110
  // ... and do a final dump before.
@@ -113,12 +115,33 @@ class App {
113
115
  return {
114
116
  async stop() {
115
117
  logger.info('Stopping the collector.');
116
- timerState.stop();
118
+ dumpTimerState.stop();
119
+ statsTimerState.stop();
117
120
  await controlServerState.stop();
118
121
  serverState.stop();
119
122
  }
120
123
  };
121
124
  }
125
+ /**
126
+ * Starts a timer that shows a message every min that no coverage
127
+ * was received until the opposite is the case.
128
+ */
129
+ static startNoMessageTimer(logger, server) {
130
+ const startTime = Date.now();
131
+ const timer = setInterval(async () => {
132
+ const stats = server.getStatistics();
133
+ if (stats.totalCoverageMessages === 0) {
134
+ logger.info(`No coverage received for ${((Date.now() - startTime) / 1000.0).toFixed(0)}s.`);
135
+ }
136
+ else {
137
+ // We can stop running the timer after we have received the first coverage.
138
+ clearInterval(timer);
139
+ }
140
+ }, 1000 * 60);
141
+ return {
142
+ stop: () => clearInterval(timer)
143
+ };
144
+ }
122
145
  /**
123
146
  * Start a timer for dumping the data, depending on the configuration.
124
147
  *
@@ -17,6 +17,14 @@ export declare class WebSocketCollectingServer {
17
17
  * The logger to use.
18
18
  */
19
19
  private readonly logger;
20
+ /**
21
+ * The number of messages that have been received.
22
+ */
23
+ private totalNumMessagesReceived;
24
+ /**
25
+ * The number of coverage messages that have been received.
26
+ */
27
+ private totalNumCoverageMessagesReceived;
20
28
  /**
21
29
  * Constructor.
22
30
  *
@@ -55,4 +63,11 @@ export declare class WebSocketCollectingServer {
55
63
  * A file name is a token; a range (start to end line) is a token.
56
64
  */
57
65
  private handleCoverageMessage;
66
+ /**
67
+ * Returns a statistic on the number of messages received.
68
+ */
69
+ getStatistics(): {
70
+ totalMessages: number;
71
+ totalCoverageMessages: number;
72
+ };
58
73
  }
@@ -46,6 +46,8 @@ class WebSocketCollectingServer {
46
46
  this.storage = commons_1.Contract.requireDefined(storage);
47
47
  this.logger = commons_1.Contract.requireDefined(logger);
48
48
  this.server = new WebSocket.Server({ port });
49
+ this.totalNumMessagesReceived = 0;
50
+ this.totalNumCoverageMessagesReceived = 0;
49
51
  }
50
52
  /**
51
53
  * Start the server socket, handle sessions and dispatch messages.
@@ -56,16 +58,17 @@ class WebSocketCollectingServer {
56
58
  // Handle new connections from clients
57
59
  (_b = this.server) === null || _b === void 0 ? void 0 : _b.on('connection', (webSocket, req) => {
58
60
  let session = new Session_1.Session(req.socket, this.storage, this.logger);
59
- this.logger.debug(`Connection from: ${req.socket.remoteAddress}`);
61
+ this.logger.trace(`Connection from: ${req.socket.remoteAddress}`);
60
62
  // Handle disconnecting clients
61
63
  webSocket.on('close', code => {
62
64
  if (session) {
63
65
  session = null;
64
- this.logger.debug(`Closing with code ${code}`);
66
+ this.logger.trace(`Closing with code ${code}`);
65
67
  }
66
68
  });
67
69
  // Handle incoming messages
68
70
  webSocket.on('message', (message) => {
71
+ this.totalNumMessagesReceived += 1;
69
72
  if (session && Buffer.isBuffer(message)) {
70
73
  void this.handleMessage(session, message);
71
74
  }
@@ -93,6 +96,7 @@ class WebSocketCollectingServer {
93
96
  try {
94
97
  const messageType = message.toString('utf8', 0, 1);
95
98
  if (messageType.startsWith(MESSAGE_TYPE_COVERAGE)) {
99
+ this.totalNumCoverageMessagesReceived += 1;
96
100
  await this.handleCoverageMessage(session, message.subarray(1));
97
101
  }
98
102
  }
@@ -140,5 +144,14 @@ class WebSocketCollectingServer {
140
144
  }
141
145
  });
142
146
  }
147
+ /**
148
+ * Returns a statistic on the number of messages received.
149
+ */
150
+ getStatistics() {
151
+ return {
152
+ totalMessages: this.totalNumMessagesReceived,
153
+ totalCoverageMessages: this.totalNumCoverageMessagesReceived
154
+ };
155
+ }
143
156
  }
144
157
  exports.WebSocketCollectingServer = WebSocketCollectingServer;
@@ -109,7 +109,7 @@ class DataStorage {
109
109
  this.coverageByProject.set(project, projectCoverage);
110
110
  }
111
111
  coveredOriginalLines.forEach(line => projectCoverage === null || projectCoverage === void 0 ? void 0 : projectCoverage.putLine(sourceFilePath, line));
112
- this.logger.debug(`Mapped Coverage: ${project} ${uniformPath} ${coveredOriginalLines}`);
112
+ this.logger.trace(`Mapped Coverage: ${project} ${uniformPath} ${coveredOriginalLines}`);
113
113
  }
114
114
  /**
115
115
  * Normalize the source file names provided by the Web browsers / from the
@@ -127,7 +127,7 @@ class DataStorage {
127
127
  // Currently only implemented to log the missing information.
128
128
  this.timesUnmappedCoverage++;
129
129
  if (this.timesUnmappedCoverage === 1) {
130
- this.logger.debug(`Received unmapped coverage for project "${project}"`);
130
+ this.logger.trace(`Received unmapped coverage for project "${project}"`);
131
131
  }
132
132
  }
133
133
  /**
@@ -1,5 +1,5 @@
1
1
  import { ConfigParameters } from '../utils/ConfigParameters';
2
- import Logger from 'bunyan';
2
+ import Logger from "bunyan";
3
3
  /**
4
4
  * Uploads a coverage file to artifactory with the provided configuration.
5
5
  */
@@ -17,7 +17,7 @@ async function uploadToArtifactory(config, logger, coverageFile, lines) {
17
17
  if (lines === 0) {
18
18
  return;
19
19
  }
20
- logger.info('Preparing upload to Artifactory');
20
+ logger.debug('Preparing upload to Artifactory');
21
21
  const form = (0, CommonUpload_1.prepareFormData)(coverageFile);
22
22
  await performArtifactoryUpload(config, form, logger);
23
23
  }
@@ -27,28 +27,35 @@ exports.prepareFormData = prepareFormData;
27
27
  * Uploads a coverage file with the provided configuration.
28
28
  */
29
29
  async function performUpload(url, form, config, uploadFunction, logger) {
30
+ var _a, _b;
30
31
  try {
31
32
  const response = await uploadFunction(url, form, config);
32
- logger.info(`Upload finished with code ${response.status}.`);
33
+ logger.debug(`Upload finished with code ${response.status}.`);
33
34
  }
34
35
  catch (error) {
35
36
  if (axios_1.default.isAxiosError(error)) {
37
+ let userMessage;
38
+ if (error.message) {
39
+ logger.error(`Upload error ${(_a = error.status) !== null && _a !== void 0 ? _a : 'UNDEFINED'}: ${error.message}`);
40
+ }
36
41
  if (error.response) {
37
- const response = error.response;
38
- if (response.status >= 400) {
39
- throw new UploadError(`Upload failed with code ${response.status}: ${response.statusText}. Response Data: ${response.data}`);
40
- }
41
- else {
42
- logger.info(`Upload with status code ${response.status} finished.`);
43
- }
42
+ // The request was made and the server responded with a status code
43
+ // that falls out of the range of 2xx
44
+ logger.error('Upload error response data:', error.response.data);
45
+ logger.error('Upload error response status:', error.response.status);
46
+ logger.error('Upload error response headers:', JSON.stringify(error.response.headers));
47
+ userMessage = `Request failed with status ${error.response.status}: ${(_b = error.response.data) !== null && _b !== void 0 ? _b : ''}`;
44
48
  }
45
49
  else if (error.request) {
46
- throw new UploadError(`Upload request did not receive a response.`);
50
+ // The request was made but no response was received
51
+ userMessage = 'No response received for the request.';
47
52
  }
48
- if (error.message) {
49
- logger.debug(`Something went wrong when uploading data: ${error.message}. Details of the error: ${(0, util_1.inspect)(error)}`);
50
- throw new UploadError(`Something went wrong when uploading data: ${error.message}`);
53
+ else {
54
+ // Something happened in setting up the request that triggered an Error
55
+ userMessage = 'Request setup failed.';
51
56
  }
57
+ // Provide a more specific message if possible
58
+ throw new UploadError(`Something went wrong when uploading data: ${userMessage}`);
52
59
  }
53
60
  throw new UploadError(`Something went wrong when uploading data: ${(0, util_1.inspect)(error)}`);
54
61
  }
@@ -18,7 +18,7 @@ async function uploadToTeamscale(config, logger, coverageFile, lines) {
18
18
  if (lines === 0) {
19
19
  return;
20
20
  }
21
- logger.info('Preparing upload to Teamscale');
21
+ logger.debug('Preparing upload to Teamscale');
22
22
  const form = (0, CommonUpload_1.prepareFormData)(coverageFile);
23
23
  const queryParameters = prepareQueryParameters(config);
24
24
  await performTeamscaleUpload(config, queryParameters, form, logger);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teamscale/coverage-collector",
3
- "version": "0.1.0-beta.4",
3
+ "version": "0.1.0-beta.6",
4
4
  "description": "Collector for JavaScript code coverage information",
5
5
  "main": "dist/src/main.js",
6
6
  "bin": "dist/src/main.js",
@@ -15,13 +15,13 @@
15
15
  "dist/**/*"
16
16
  ],
17
17
  "dependencies": {
18
- "@cqse/commons": "0.1.0-beta.2",
18
+ "@cqse/commons": "0.1.0-beta.5",
19
19
  "argparse": "^2.0.1",
20
20
  "async": "^3.2.5",
21
- "axios": "^1.6.5",
21
+ "axios": "^1.6.7",
22
22
  "bunyan": "^1.8.15",
23
- "date-and-time": "^3.1.0",
24
- "dotenv": "^16.3.1",
23
+ "date-and-time": "^3.1.1",
24
+ "dotenv": "^16.4.5",
25
25
  "express": "^4.18.2",
26
26
  "form-data": "^4.0.0",
27
27
  "mkdirp": "^3.0.1",
@@ -32,22 +32,22 @@
32
32
  "ws": "^8.16.0"
33
33
  },
34
34
  "devDependencies": {
35
- "@babel/core": "^7.23.7",
36
- "@babel/preset-env": "^7.23.8",
35
+ "@babel/core": "^7.23.9",
36
+ "@babel/preset-env": "^7.23.9",
37
37
  "@types/argparse": "^2.0.14",
38
38
  "@types/async": "^3.2.24",
39
39
  "@types/bunyan": "^1.8.11",
40
40
  "@types/express": "^4.17.21",
41
- "@types/jest": "^29.5.11",
42
- "@types/node": "^20.11.0",
41
+ "@types/jest": "^29.5.12",
42
+ "@types/node": "^20.11.19",
43
43
  "@types/tmp": "^0.2.6",
44
44
  "@types/ws": "^8.5.10",
45
45
  "babel-jest": "^29.7.0",
46
- "esbuild": "^0.19.11",
46
+ "esbuild": "^0.20.1",
47
47
  "jest": "^29.7.0",
48
48
  "mockttp": "^3.10.1",
49
49
  "rimraf": "^5.0.5",
50
- "ts-jest": "^29.1.1",
50
+ "ts-jest": "^29.1.2",
51
51
  "ts-node": "^10.9.2",
52
52
  "typescript": "^5.3.3"
53
53
  },