dd-trace 5.105.0 → 5.107.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/index.d.ts +20 -1
- package/package.json +5 -7
- package/packages/datadog-core/src/storage.js +47 -48
- package/packages/datadog-esbuild/index.js +6 -1
- package/packages/datadog-instrumentations/src/ai.js +12 -3
- package/packages/datadog-instrumentations/src/aws-sdk.js +3 -2
- package/packages/datadog-instrumentations/src/body-parser.js +5 -2
- package/packages/datadog-instrumentations/src/connect.js +3 -2
- package/packages/datadog-instrumentations/src/cookie-parser.js +3 -2
- package/packages/datadog-instrumentations/src/cucumber-worker-threads.js +19 -0
- package/packages/datadog-instrumentations/src/cucumber.js +319 -152
- package/packages/datadog-instrumentations/src/express-mongo-sanitize.js +7 -5
- package/packages/datadog-instrumentations/src/express-session.js +12 -11
- package/packages/datadog-instrumentations/src/express.js +24 -20
- package/packages/datadog-instrumentations/src/fastify.js +18 -6
- package/packages/datadog-instrumentations/src/helpers/openai-ai-guard.js +27 -12
- package/packages/datadog-instrumentations/src/http/client.js +9 -12
- package/packages/datadog-instrumentations/src/http/server.js +30 -16
- package/packages/datadog-instrumentations/src/http2/client.js +15 -12
- package/packages/datadog-instrumentations/src/http2/server.js +15 -8
- package/packages/datadog-instrumentations/src/jest/bail-reporter.js +42 -0
- package/packages/datadog-instrumentations/src/jest.js +143 -73
- package/packages/datadog-instrumentations/src/mocha/main.js +43 -8
- package/packages/datadog-instrumentations/src/mocha/utils.js +128 -17
- package/packages/datadog-instrumentations/src/multer.js +3 -2
- package/packages/datadog-instrumentations/src/mysql2.js +34 -0
- package/packages/datadog-instrumentations/src/net.js +8 -6
- package/packages/datadog-instrumentations/src/openai.js +19 -7
- package/packages/datadog-instrumentations/src/pg.js +19 -0
- package/packages/datadog-instrumentations/src/router.js +12 -10
- package/packages/datadog-instrumentations/src/vitest.js +29 -4
- package/packages/datadog-plugin-aws-sdk/src/base.js +0 -3
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/tracing.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +218 -4
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +62 -11
- package/packages/datadog-plugin-cucumber/src/index.js +2 -0
- package/packages/datadog-plugin-cypress/src/support.js +31 -1
- package/packages/datadog-plugin-http/src/client.js +0 -3
- package/packages/datadog-plugin-http/src/server.js +11 -1
- package/packages/datadog-plugin-mocha/src/index.js +2 -0
- package/packages/datadog-plugin-pg/src/index.js +10 -0
- package/packages/dd-trace/src/aiguard/index.js +34 -15
- package/packages/dd-trace/src/aiguard/sdk.js +34 -3
- package/packages/dd-trace/src/aiguard/tags.js +6 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +1 -1
- package/packages/dd-trace/src/config/defaults.js +14 -0
- package/packages/dd-trace/src/config/generated-config-types.d.ts +1 -1
- package/packages/dd-trace/src/config/helper.js +1 -0
- package/packages/dd-trace/src/config/index.js +5 -9
- package/packages/dd-trace/src/config/parsers.js +8 -0
- package/packages/dd-trace/src/config/supported-configurations.json +13 -6
- package/packages/dd-trace/src/crashtracking/crashtracker.js +2 -2
- package/packages/dd-trace/src/datastreams/writer.js +1 -2
- package/packages/dd-trace/src/debugger/config.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/config.js +3 -2
- package/packages/dd-trace/src/debugger/index.js +1 -2
- package/packages/dd-trace/src/dogstatsd.js +2 -3
- package/packages/dd-trace/src/encode/0.4.js +49 -41
- package/packages/dd-trace/src/encode/agentless-json.js +5 -1
- package/packages/dd-trace/src/encode/tags-processors.js +14 -0
- package/packages/dd-trace/src/exporters/agent/index.js +1 -2
- package/packages/dd-trace/src/exporters/agentless/index.js +6 -10
- package/packages/dd-trace/src/exporters/common/buffering-exporter.js +1 -2
- package/packages/dd-trace/src/exporters/common/request.js +26 -0
- package/packages/dd-trace/src/exporters/span-stats/index.js +1 -2
- package/packages/dd-trace/src/id.js +15 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +91 -5
- package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +43 -21
- package/packages/dd-trace/src/llmobs/plugins/genai/index.js +4 -0
- package/packages/dd-trace/src/llmobs/plugins/genai/util.js +45 -0
- package/packages/dd-trace/src/llmobs/sdk.js +4 -1
- package/packages/dd-trace/src/llmobs/span_processor.js +17 -1
- package/packages/dd-trace/src/llmobs/tagger.js +5 -3
- package/packages/dd-trace/src/llmobs/util.js +54 -0
- package/packages/dd-trace/src/llmobs/writers/base.js +1 -2
- package/packages/dd-trace/src/llmobs/writers/util.js +1 -2
- package/packages/dd-trace/src/openfeature/writers/base.js +1 -10
- package/packages/dd-trace/src/openfeature/writers/util.js +1 -2
- package/packages/dd-trace/src/opentelemetry/metrics/instruments.js +26 -13
- package/packages/dd-trace/src/opentelemetry/metrics/meter.js +7 -10
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +92 -0
- package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +25 -5
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +2 -10
- package/packages/dd-trace/src/opentracing/span.js +23 -18
- package/packages/dd-trace/src/opentracing/span_context.js +1 -3
- package/packages/dd-trace/src/opentracing/tracer.js +16 -12
- package/packages/dd-trace/src/plugins/ci_plugin.js +131 -46
- package/packages/dd-trace/src/priority_sampler.js +6 -5
- package/packages/dd-trace/src/profiling/config.js +11 -25
- package/packages/dd-trace/src/profiling/exporters/agent.js +11 -10
- package/packages/dd-trace/src/profiling/profiler.js +19 -9
- package/packages/dd-trace/src/profiling/profilers/wall.js +2 -3
- package/packages/dd-trace/src/proxy.js +13 -10
- package/packages/dd-trace/src/remote_config/index.js +1 -2
- package/packages/dd-trace/src/runtime_metrics/client.js +30 -0
- package/packages/dd-trace/src/runtime_metrics/index.js +12 -2
- package/packages/dd-trace/src/runtime_metrics/otlp_runtime_metrics.js +284 -0
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +2 -11
- package/packages/dd-trace/src/service-naming/source-resolver.js +5 -1
- package/packages/dd-trace/src/span_format.js +33 -25
- package/packages/dd-trace/src/span_stats.js +1 -1
- package/packages/dd-trace/src/startup-log.js +1 -2
- package/packages/dd-trace/src/telemetry/send-data.js +1 -1
- package/packages/dd-trace/src/tracer.js +1 -1
- package/vendor/dist/@apm-js-collab/code-transformer/index.js +2 -2
- package/vendor/dist/shell-quote/index.js +1 -1
- package/packages/dd-trace/src/agent/url.js +0 -28
- package/scripts/preinstall.js +0 -34
|
@@ -30,6 +30,8 @@ const { writeCoverageBackfillToCache } = require('../../dd-trace/src/ci-visibili
|
|
|
30
30
|
const satisfies = require('../../../vendor/dist/semifies')
|
|
31
31
|
const { addHook, channel } = require('./helpers/instrument')
|
|
32
32
|
|
|
33
|
+
const cucumberWorkerThreadsPatchModule = require.resolve('./cucumber-worker-threads')
|
|
34
|
+
|
|
33
35
|
const testStartCh = channel('ci:cucumber:test:start')
|
|
34
36
|
const testRetryCh = channel('ci:cucumber:test:retry')
|
|
35
37
|
const testFinishCh = channel('ci:cucumber:test:finish') // used for test steps too
|
|
@@ -69,6 +71,7 @@ const originalCoverageMap = createCoverageMap()
|
|
|
69
71
|
|
|
70
72
|
// TODO: remove in a later major version
|
|
71
73
|
const patched = new WeakSet()
|
|
74
|
+
const patchedCucumberWorkers = new WeakSet()
|
|
72
75
|
|
|
73
76
|
const lastStatusByPickleId = new Map()
|
|
74
77
|
/** For ATR: statuses keyed by stable scenario id (uri:name) so retries accumulate correctly */
|
|
@@ -180,22 +183,270 @@ function getConfiguredEfdRetryCount () {
|
|
|
180
183
|
}
|
|
181
184
|
|
|
182
185
|
function publishWorkerEfdRetryCount (pickle, retryCount) {
|
|
183
|
-
|
|
186
|
+
const message = {
|
|
187
|
+
[DD_EFD_RETRY_COUNT_MESSAGE]: {
|
|
188
|
+
pickleId: pickle.id,
|
|
189
|
+
retryCount,
|
|
190
|
+
testFileAbsolutePath: pickle.uri,
|
|
191
|
+
testName: pickle.name,
|
|
192
|
+
},
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (typeof process.send === 'function') {
|
|
196
|
+
try {
|
|
197
|
+
process.send(message)
|
|
198
|
+
} catch {
|
|
199
|
+
// ignore IPC errors
|
|
200
|
+
}
|
|
201
|
+
return
|
|
202
|
+
}
|
|
184
203
|
|
|
185
204
|
try {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
testFileAbsolutePath: pickle.uri,
|
|
191
|
-
testName: pickle.name,
|
|
192
|
-
},
|
|
193
|
-
})
|
|
205
|
+
const { isMainThread, parentPort } = require('node:worker_threads')
|
|
206
|
+
if (isMainThread || !parentPort) return
|
|
207
|
+
|
|
208
|
+
parentPort.postMessage(message)
|
|
194
209
|
} catch {
|
|
195
210
|
// ignore IPC errors
|
|
196
211
|
}
|
|
197
212
|
}
|
|
198
213
|
|
|
214
|
+
function configureParallelWorkerWorldParameters (options) {
|
|
215
|
+
options.worldParameters ??= {}
|
|
216
|
+
|
|
217
|
+
if (isKnownTestsEnabled && isValidKnownTests(knownTests)) {
|
|
218
|
+
options.worldParameters._ddIsKnownTestsEnabled = true
|
|
219
|
+
options.worldParameters._ddIsEarlyFlakeDetectionEnabled = isEarlyFlakeDetectionEnabled
|
|
220
|
+
options.worldParameters._ddKnownTests = knownTests
|
|
221
|
+
options.worldParameters._ddEarlyFlakeDetectionNumRetries = earlyFlakeDetectionNumRetries
|
|
222
|
+
options.worldParameters._ddEarlyFlakeDetectionSlowTestRetries = earlyFlakeDetectionSlowTestRetries
|
|
223
|
+
} else {
|
|
224
|
+
isEarlyFlakeDetectionEnabled = false
|
|
225
|
+
isKnownTestsEnabled = false
|
|
226
|
+
options.worldParameters._ddIsEarlyFlakeDetectionEnabled = false
|
|
227
|
+
options.worldParameters._ddIsKnownTestsEnabled = false
|
|
228
|
+
options.worldParameters._ddEarlyFlakeDetectionNumRetries = 0
|
|
229
|
+
options.worldParameters._ddEarlyFlakeDetectionSlowTestRetries = {}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (isImpactedTestsEnabled) {
|
|
233
|
+
options.worldParameters._ddImpactedTestsEnabled = isImpactedTestsEnabled
|
|
234
|
+
options.worldParameters._ddModifiedFiles = modifiedFiles
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
options.worldParameters._ddIsFlakyTestRetriesEnabled = isFlakyTestRetriesEnabled
|
|
238
|
+
options.worldParameters._ddNumTestRetries = numTestRetries
|
|
239
|
+
|
|
240
|
+
if (isTestManagementTestsEnabled) {
|
|
241
|
+
options.worldParameters._ddIsTestManagementTestsEnabled = true
|
|
242
|
+
options.worldParameters._ddTestManagementTests = testManagementTests
|
|
243
|
+
options.worldParameters._ddTestManagementAttemptToFixRetries = testManagementAttemptToFixRetries
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function readParallelWorkerWorldParameters (options) {
|
|
248
|
+
const worldParameters = options?.worldParameters
|
|
249
|
+
if (!worldParameters) return
|
|
250
|
+
|
|
251
|
+
isKnownTestsEnabled = !!worldParameters._ddIsKnownTestsEnabled
|
|
252
|
+
if (isKnownTestsEnabled) {
|
|
253
|
+
knownTests = worldParameters._ddKnownTests
|
|
254
|
+
// if for whatever reason the worker does not receive valid known tests, we disable EFD and known tests
|
|
255
|
+
if (!isValidKnownTests(knownTests)) {
|
|
256
|
+
isKnownTestsEnabled = false
|
|
257
|
+
knownTests = {}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
isEarlyFlakeDetectionEnabled = !!worldParameters._ddIsEarlyFlakeDetectionEnabled
|
|
261
|
+
if (isEarlyFlakeDetectionEnabled) {
|
|
262
|
+
earlyFlakeDetectionNumRetries = worldParameters._ddEarlyFlakeDetectionNumRetries
|
|
263
|
+
earlyFlakeDetectionSlowTestRetries = worldParameters._ddEarlyFlakeDetectionSlowTestRetries ?? {}
|
|
264
|
+
}
|
|
265
|
+
isImpactedTestsEnabled = !!worldParameters._ddImpactedTestsEnabled
|
|
266
|
+
if (isImpactedTestsEnabled) {
|
|
267
|
+
modifiedFiles = worldParameters._ddModifiedFiles
|
|
268
|
+
}
|
|
269
|
+
isFlakyTestRetriesEnabled = !!worldParameters._ddIsFlakyTestRetriesEnabled
|
|
270
|
+
numTestRetries = worldParameters._ddNumTestRetries ?? 0
|
|
271
|
+
isTestManagementTestsEnabled = !!worldParameters._ddIsTestManagementTestsEnabled
|
|
272
|
+
if (isTestManagementTestsEnabled) {
|
|
273
|
+
testManagementTests = worldParameters._ddTestManagementTests
|
|
274
|
+
testManagementAttemptToFixRetries = worldParameters._ddTestManagementAttemptToFixRetries
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function handleDdWorkerMessage (message) {
|
|
279
|
+
if (Array.isArray(message)) {
|
|
280
|
+
const [messageCode, payload] = message
|
|
281
|
+
if (messageCode === CUCUMBER_WORKER_TRACE_PAYLOAD_CODE) {
|
|
282
|
+
collectAttemptToFixExecutionsFromTraces(payload, attemptToFixExecutions)
|
|
283
|
+
workerReportTraceCh.publish(payload)
|
|
284
|
+
return true
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (message?.[DD_EFD_RETRY_COUNT_MESSAGE]) {
|
|
289
|
+
handleEfdRetryCountMessage(message[DD_EFD_RETRY_COUNT_MESSAGE])
|
|
290
|
+
return true
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return false
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function onCucumberWorkerThreadMessage (message) {
|
|
297
|
+
if (!testSuiteFinishCh.hasSubscribers) return
|
|
298
|
+
|
|
299
|
+
handleDdWorkerMessage(message)
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function registerWorkerThreadMessageHandlers (workers) {
|
|
303
|
+
if (!workers) return
|
|
304
|
+
|
|
305
|
+
for (const worker of workers) {
|
|
306
|
+
worker.workerThread.on('message', onCucumberWorkerThreadMessage)
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function registerWorkerThreadPatchModule (supportCodeLibrary) {
|
|
311
|
+
const requireModules = supportCodeLibrary?.originalCoordinates?.requireModules
|
|
312
|
+
if (!Array.isArray(requireModules) || requireModules.includes(cucumberWorkerThreadsPatchModule)) return
|
|
313
|
+
|
|
314
|
+
requireModules.unshift(cucumberWorkerThreadsPatchModule)
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function getRunningAssembledTestCase (adapter, worker) {
|
|
318
|
+
const command = adapter.running?.get(worker)
|
|
319
|
+
return command?.assembledTestCase
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function maybeStartParallelSuite (pickle) {
|
|
323
|
+
if (!pickle) return
|
|
324
|
+
|
|
325
|
+
const testFileAbsolutePath = pickle.uri
|
|
326
|
+
if (pickleResultByFile[testFileAbsolutePath]) return
|
|
327
|
+
|
|
328
|
+
pickleResultByFile[testFileAbsolutePath] = []
|
|
329
|
+
testSuiteStartCh.publish({
|
|
330
|
+
testFileAbsolutePath,
|
|
331
|
+
})
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function handleParallelTestCaseFinished (pickle, worstTestStepResult) {
|
|
335
|
+
const { status } = getStatusFromResultLatest(worstTestStepResult)
|
|
336
|
+
let isNew = false
|
|
337
|
+
|
|
338
|
+
if (isKnownTestsEnabled) {
|
|
339
|
+
isNew = isNewTest(pickle.uri, pickle.name)
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const testFileAbsolutePath = pickle.uri
|
|
343
|
+
const finished = pickleResultByFile[testFileAbsolutePath] || (pickleResultByFile[testFileAbsolutePath] = [])
|
|
344
|
+
|
|
345
|
+
if (isEarlyFlakeDetectionEnabled && isNew) {
|
|
346
|
+
const testFullname = `${pickle.uri}:${pickle.name}`
|
|
347
|
+
let testStatuses = newTestsByTestFullname.get(testFullname)
|
|
348
|
+
if (testStatuses) {
|
|
349
|
+
testStatuses.push(status)
|
|
350
|
+
} else {
|
|
351
|
+
testStatuses = [status]
|
|
352
|
+
newTestsByTestFullname.set(testFullname, testStatuses)
|
|
353
|
+
}
|
|
354
|
+
let efdRetryCount = efdRetryCountByPickleId.get(pickle.id)
|
|
355
|
+
if (efdRetryCount === undefined) {
|
|
356
|
+
efdRetryCount = status === 'skip'
|
|
357
|
+
? 0
|
|
358
|
+
: getConfiguredEfdRetryCount()
|
|
359
|
+
efdRetryCountByPickleId.set(pickle.id, efdRetryCount)
|
|
360
|
+
if (efdRetryCount === 0 && status !== 'skip') {
|
|
361
|
+
efdSlowAbortedPickleIds.add(pickle.id)
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
maybeRecordFinalParallelEfdStatus({ pickleId: pickle.id, testFileAbsolutePath, testFullname })
|
|
365
|
+
} else if (
|
|
366
|
+
isTestManagementTestsEnabled &&
|
|
367
|
+
getTestProperties(getTestSuitePath(testFileAbsolutePath, process.cwd()), pickle.name).attemptToFix
|
|
368
|
+
) {
|
|
369
|
+
const testFullname = `${pickle.uri}:${pickle.name}`
|
|
370
|
+
let testStatuses = attemptToFixTestsByTestFullname.get(testFullname)
|
|
371
|
+
if (testStatuses) {
|
|
372
|
+
testStatuses.push(status)
|
|
373
|
+
} else {
|
|
374
|
+
testStatuses = [status]
|
|
375
|
+
attemptToFixTestsByTestFullname.set(testFullname, testStatuses)
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if (status === 'skip' || testStatuses.length === testManagementAttemptToFixRetries + 1) {
|
|
379
|
+
finished.push(getTestStatusFromAttemptToFixExecutions(testStatuses))
|
|
380
|
+
attemptToFixTestsByTestFullname.delete(testFullname)
|
|
381
|
+
}
|
|
382
|
+
} else {
|
|
383
|
+
// TODO: can we get error message?
|
|
384
|
+
finished.push(status)
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
finishParallelSuiteIfDone(testFileAbsolutePath)
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function getWrappedHandleWorkerThreadEvent (handleEventFromWorker) {
|
|
391
|
+
return function (worker, event) {
|
|
392
|
+
if (!testSuiteFinishCh.hasSubscribers) {
|
|
393
|
+
return handleEventFromWorker.apply(this, arguments)
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (handleDdWorkerMessage(event)) return
|
|
397
|
+
|
|
398
|
+
const envelope = event?.type === 'ENVELOPE' && event.envelope
|
|
399
|
+
if (!envelope) {
|
|
400
|
+
return handleEventFromWorker.apply(this, arguments)
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const assembledTestCase = getRunningAssembledTestCase(this, worker)
|
|
404
|
+
|
|
405
|
+
if (envelope.testCaseStarted) {
|
|
406
|
+
maybeStartParallelSuite(assembledTestCase?.pickle)
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
const result = handleEventFromWorker.apply(this, arguments)
|
|
410
|
+
|
|
411
|
+
if (envelope.testCaseFinished && assembledTestCase?.pickle && eventDataCollector) {
|
|
412
|
+
const worstTestStepResult =
|
|
413
|
+
eventDataCollector.getTestCaseAttempt(envelope.testCaseFinished.testCaseStartedId).worstTestStepResult
|
|
414
|
+
handleParallelTestCaseFinished(assembledTestCase.pickle, worstTestStepResult)
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
return result
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
function getWrappedWorkerThreadsSetup (setup) {
|
|
422
|
+
return async function () {
|
|
423
|
+
if (testSuiteFinishCh.hasSubscribers) {
|
|
424
|
+
configureParallelWorkerWorldParameters(this.options)
|
|
425
|
+
registerWorkerThreadPatchModule(this.supportCodeLibrary)
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
const result = await setup.apply(this, arguments)
|
|
429
|
+
|
|
430
|
+
if (testSuiteFinishCh.hasSubscribers) {
|
|
431
|
+
registerWorkerThreadMessageHandlers(this.workers)
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
return result
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
function getWrappedWorkerThreadsTeardown (teardown) {
|
|
439
|
+
return function () {
|
|
440
|
+
if (testSuiteFinishCh.hasSubscribers && this.workers) {
|
|
441
|
+
for (const worker of this.workers) {
|
|
442
|
+
worker.workerThread.removeListener('message', onCucumberWorkerThreadMessage)
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
return teardown.apply(this, arguments)
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
199
450
|
function finishParallelSuiteIfDone (testFileAbsolutePath) {
|
|
200
451
|
const finished = pickleResultByFile[testFileAbsolutePath]
|
|
201
452
|
const expectedPickles = pickleByFile[testFileAbsolutePath]
|
|
@@ -929,6 +1180,9 @@ function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = fa
|
|
|
929
1180
|
if (!testSuiteFinishCh.hasSubscribers) {
|
|
930
1181
|
return runTestCaseFunction.apply(this, arguments)
|
|
931
1182
|
}
|
|
1183
|
+
if (isWorker) {
|
|
1184
|
+
readParallelWorkerWorldParameters(this.options)
|
|
1185
|
+
}
|
|
932
1186
|
const pickle = isNewerCucumberVersion
|
|
933
1187
|
? arguments[0].pickle
|
|
934
1188
|
: this.eventDataCollector.getPickle(arguments[0])
|
|
@@ -998,6 +1252,11 @@ function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = fa
|
|
|
998
1252
|
numRetriesByPickleId.set(pickle.id, 0)
|
|
999
1253
|
}
|
|
1000
1254
|
}
|
|
1255
|
+
const originalRetry = this.options.retry
|
|
1256
|
+
const isManagedRetry = isAttemptToFix || (isEarlyFlakeDetectionEnabled && (isNew || isModified))
|
|
1257
|
+
if (isManagedRetry) {
|
|
1258
|
+
this.options.retry = 0
|
|
1259
|
+
}
|
|
1001
1260
|
// TODO: for >=11 we could use `runTestCaseResult` instead of accumulating results in `lastStatusByPickleId`
|
|
1002
1261
|
const firstExecutionStart = performance.now()
|
|
1003
1262
|
let runTestCaseResult = await runTestCaseFunction.apply(this, arguments)
|
|
@@ -1097,6 +1356,8 @@ function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = fa
|
|
|
1097
1356
|
testSuiteFinishCh.publish({ status: testSuiteStatus, testSuitePath })
|
|
1098
1357
|
}
|
|
1099
1358
|
|
|
1359
|
+
this.options.retry = originalRetry
|
|
1360
|
+
|
|
1100
1361
|
if (isNewerCucumberVersion && isEarlyFlakeDetectionEnabled && (isNew || isModified)) {
|
|
1101
1362
|
return shouldBePassedByEFD
|
|
1102
1363
|
}
|
|
@@ -1113,26 +1374,25 @@ function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = fa
|
|
|
1113
1374
|
}
|
|
1114
1375
|
}
|
|
1115
1376
|
|
|
1377
|
+
function patchCucumberWorkerRunTestCase (workerPackage, isWorker) {
|
|
1378
|
+
const workerPrototype = workerPackage?.Worker?.prototype
|
|
1379
|
+
if (!workerPrototype || patchedCucumberWorkers.has(workerPrototype)) return
|
|
1380
|
+
|
|
1381
|
+
patchedCucumberWorkers.add(workerPrototype)
|
|
1382
|
+
shimmer.wrap(
|
|
1383
|
+
workerPrototype,
|
|
1384
|
+
'runTestCase',
|
|
1385
|
+
runTestCase => getWrappedRunTestCase(runTestCase, true, isWorker)
|
|
1386
|
+
)
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1116
1389
|
function getWrappedParseWorkerMessage (parseWorkerMessageFunction, isNewVersion) {
|
|
1117
1390
|
return function (worker, message) {
|
|
1118
1391
|
if (!testSuiteFinishCh.hasSubscribers) {
|
|
1119
1392
|
return parseWorkerMessageFunction.apply(this, arguments)
|
|
1120
1393
|
}
|
|
1121
|
-
// If
|
|
1122
|
-
|
|
1123
|
-
// TODO: identify the message better
|
|
1124
|
-
if (Array.isArray(message)) {
|
|
1125
|
-
const [messageCode, payload] = message
|
|
1126
|
-
if (messageCode === CUCUMBER_WORKER_TRACE_PAYLOAD_CODE) {
|
|
1127
|
-
collectAttemptToFixExecutionsFromTraces(payload, attemptToFixExecutions)
|
|
1128
|
-
workerReportTraceCh.publish(payload)
|
|
1129
|
-
return
|
|
1130
|
-
}
|
|
1131
|
-
}
|
|
1132
|
-
if (message[DD_EFD_RETRY_COUNT_MESSAGE]) {
|
|
1133
|
-
handleEfdRetryCountMessage(message[DD_EFD_RETRY_COUNT_MESSAGE])
|
|
1134
|
-
return
|
|
1135
|
-
}
|
|
1394
|
+
// If it's a dd-trace message, stop cucumber processing or cucumber will throw an error.
|
|
1395
|
+
if (handleDdWorkerMessage(message)) return
|
|
1136
1396
|
|
|
1137
1397
|
const envelope = isNewVersion ? message.envelope : message.jsonEnvelope
|
|
1138
1398
|
|
|
@@ -1149,10 +1409,7 @@ function getWrappedParseWorkerMessage (parseWorkerMessageFunction, isNewVersion)
|
|
|
1149
1409
|
return parseWorkerMessageFunction.apply(this, arguments)
|
|
1150
1410
|
}
|
|
1151
1411
|
}
|
|
1152
|
-
if (parsed
|
|
1153
|
-
handleEfdRetryCountMessage(parsed[DD_EFD_RETRY_COUNT_MESSAGE])
|
|
1154
|
-
return
|
|
1155
|
-
}
|
|
1412
|
+
if (handleDdWorkerMessage(parsed)) return
|
|
1156
1413
|
let pickle
|
|
1157
1414
|
|
|
1158
1415
|
if (parsed.testCaseStarted) {
|
|
@@ -1162,15 +1419,7 @@ function getWrappedParseWorkerMessage (parseWorkerMessageFunction, isNewVersion)
|
|
|
1162
1419
|
const { pickleId } = this.eventDataCollector.testCaseMap[parsed.testCaseStarted.testCaseId]
|
|
1163
1420
|
pickle = this.eventDataCollector.getPickle(pickleId)
|
|
1164
1421
|
}
|
|
1165
|
-
|
|
1166
|
-
const testFileAbsolutePath = pickle.uri
|
|
1167
|
-
// First test in suite
|
|
1168
|
-
if (!pickleResultByFile[testFileAbsolutePath]) {
|
|
1169
|
-
pickleResultByFile[testFileAbsolutePath] = []
|
|
1170
|
-
testSuiteStartCh.publish({
|
|
1171
|
-
testFileAbsolutePath,
|
|
1172
|
-
})
|
|
1173
|
-
}
|
|
1422
|
+
maybeStartParallelSuite(pickle)
|
|
1174
1423
|
}
|
|
1175
1424
|
|
|
1176
1425
|
const parseWorkerResponse = parseWorkerMessageFunction.apply(this, arguments)
|
|
@@ -1188,66 +1437,15 @@ function getWrappedParseWorkerMessage (parseWorkerMessageFunction, isNewVersion)
|
|
|
1188
1437
|
pickle = testCase.pickle
|
|
1189
1438
|
}
|
|
1190
1439
|
|
|
1191
|
-
|
|
1192
|
-
let isNew = false
|
|
1193
|
-
|
|
1194
|
-
if (isKnownTestsEnabled) {
|
|
1195
|
-
isNew = isNewTest(pickle.uri, pickle.name)
|
|
1196
|
-
}
|
|
1197
|
-
|
|
1198
|
-
const testFileAbsolutePath = pickle.uri
|
|
1199
|
-
const finished = pickleResultByFile[testFileAbsolutePath]
|
|
1200
|
-
|
|
1201
|
-
if (isEarlyFlakeDetectionEnabled && isNew) {
|
|
1202
|
-
const testFullname = `${pickle.uri}:${pickle.name}`
|
|
1203
|
-
let testStatuses = newTestsByTestFullname.get(testFullname)
|
|
1204
|
-
if (testStatuses) {
|
|
1205
|
-
testStatuses.push(status)
|
|
1206
|
-
} else {
|
|
1207
|
-
testStatuses = [status]
|
|
1208
|
-
newTestsByTestFullname.set(testFullname, testStatuses)
|
|
1209
|
-
}
|
|
1210
|
-
let efdRetryCount = efdRetryCountByPickleId.get(pickle.id)
|
|
1211
|
-
if (efdRetryCount === undefined) {
|
|
1212
|
-
efdRetryCount = status === 'skip'
|
|
1213
|
-
? 0
|
|
1214
|
-
: getConfiguredEfdRetryCount()
|
|
1215
|
-
efdRetryCountByPickleId.set(pickle.id, efdRetryCount)
|
|
1216
|
-
if (efdRetryCount === 0 && status !== 'skip') {
|
|
1217
|
-
efdSlowAbortedPickleIds.add(pickle.id)
|
|
1218
|
-
}
|
|
1219
|
-
}
|
|
1220
|
-
maybeRecordFinalParallelEfdStatus({ pickleId: pickle.id, testFileAbsolutePath, testFullname })
|
|
1221
|
-
} else if (
|
|
1222
|
-
isTestManagementTestsEnabled &&
|
|
1223
|
-
getTestProperties(getTestSuitePath(testFileAbsolutePath, process.cwd()), pickle.name).attemptToFix
|
|
1224
|
-
) {
|
|
1225
|
-
const testFullname = `${pickle.uri}:${pickle.name}`
|
|
1226
|
-
let testStatuses = attemptToFixTestsByTestFullname.get(testFullname)
|
|
1227
|
-
if (testStatuses) {
|
|
1228
|
-
testStatuses.push(status)
|
|
1229
|
-
} else {
|
|
1230
|
-
testStatuses = [status]
|
|
1231
|
-
attemptToFixTestsByTestFullname.set(testFullname, testStatuses)
|
|
1232
|
-
}
|
|
1233
|
-
|
|
1234
|
-
if (status === 'skip' || testStatuses.length === testManagementAttemptToFixRetries + 1) {
|
|
1235
|
-
finished.push(getTestStatusFromAttemptToFixExecutions(testStatuses))
|
|
1236
|
-
attemptToFixTestsByTestFullname.delete(testFullname)
|
|
1237
|
-
}
|
|
1238
|
-
} else {
|
|
1239
|
-
// TODO: can we get error message?
|
|
1240
|
-
const finished = pickleResultByFile[testFileAbsolutePath]
|
|
1241
|
-
finished.push(status)
|
|
1242
|
-
}
|
|
1243
|
-
|
|
1244
|
-
finishParallelSuiteIfDone(testFileAbsolutePath)
|
|
1440
|
+
handleParallelTestCaseFinished(pickle, worstTestStepResult)
|
|
1245
1441
|
}
|
|
1246
1442
|
|
|
1247
1443
|
return parseWorkerResponse
|
|
1248
1444
|
}
|
|
1249
1445
|
}
|
|
1250
1446
|
|
|
1447
|
+
module.exports.patchCucumberWorkerRunTestCase = patchCucumberWorkerRunTestCase
|
|
1448
|
+
|
|
1251
1449
|
// Test start / finish for older versions. The only hook executed in workers when in parallel mode
|
|
1252
1450
|
addHook({
|
|
1253
1451
|
name: '@cucumber/cucumber',
|
|
@@ -1319,11 +1517,7 @@ addHook({
|
|
|
1319
1517
|
versions: ['>=11.0.0'],
|
|
1320
1518
|
file: 'lib/runtime/worker.js',
|
|
1321
1519
|
}, (workerPackage) => {
|
|
1322
|
-
|
|
1323
|
-
workerPackage.Worker.prototype,
|
|
1324
|
-
'runTestCase',
|
|
1325
|
-
runTestCase => getWrappedRunTestCase(runTestCase, true, !!getEnvironmentVariable('CUCUMBER_WORKER_ID'))
|
|
1326
|
-
)
|
|
1520
|
+
patchCucumberWorkerRunTestCase(workerPackage, !!getEnvironmentVariable('CUCUMBER_WORKER_ID'))
|
|
1327
1521
|
return workerPackage
|
|
1328
1522
|
})
|
|
1329
1523
|
|
|
@@ -1359,7 +1553,7 @@ addHook({
|
|
|
1359
1553
|
// In `startWorker` we pass early flake detection info to the worker.
|
|
1360
1554
|
addHook({
|
|
1361
1555
|
name: '@cucumber/cucumber',
|
|
1362
|
-
versions: ['>=11.0.0'],
|
|
1556
|
+
versions: ['>=11.0.0 <13.0.0'],
|
|
1363
1557
|
file: 'lib/runtime/parallel/adapter.js',
|
|
1364
1558
|
}, (adapterPackage) => {
|
|
1365
1559
|
shimmer.wrap(
|
|
@@ -1369,46 +1563,43 @@ addHook({
|
|
|
1369
1563
|
)
|
|
1370
1564
|
// EFD in parallel mode only supported in >=11.0.0
|
|
1371
1565
|
shimmer.wrap(adapterPackage.ChildProcessAdapter.prototype, 'startWorker', startWorker => function (...args) {
|
|
1372
|
-
|
|
1373
|
-
this.options.worldParameters._ddIsKnownTestsEnabled = true
|
|
1374
|
-
this.options.worldParameters._ddIsEarlyFlakeDetectionEnabled = isEarlyFlakeDetectionEnabled
|
|
1375
|
-
this.options.worldParameters._ddKnownTests = knownTests
|
|
1376
|
-
this.options.worldParameters._ddEarlyFlakeDetectionNumRetries = earlyFlakeDetectionNumRetries
|
|
1377
|
-
this.options.worldParameters._ddEarlyFlakeDetectionSlowTestRetries = earlyFlakeDetectionSlowTestRetries
|
|
1378
|
-
} else {
|
|
1379
|
-
isEarlyFlakeDetectionEnabled = false
|
|
1380
|
-
isKnownTestsEnabled = false
|
|
1381
|
-
this.options.worldParameters._ddIsEarlyFlakeDetectionEnabled = false
|
|
1382
|
-
this.options.worldParameters._ddIsKnownTestsEnabled = false
|
|
1383
|
-
this.options.worldParameters._ddEarlyFlakeDetectionNumRetries = 0
|
|
1384
|
-
this.options.worldParameters._ddEarlyFlakeDetectionSlowTestRetries = {}
|
|
1385
|
-
}
|
|
1386
|
-
|
|
1387
|
-
if (isImpactedTestsEnabled) {
|
|
1388
|
-
this.options.worldParameters._ddImpactedTestsEnabled = isImpactedTestsEnabled
|
|
1389
|
-
this.options.worldParameters._ddModifiedFiles = modifiedFiles
|
|
1390
|
-
}
|
|
1391
|
-
|
|
1392
|
-
this.options.worldParameters._ddIsFlakyTestRetriesEnabled = isFlakyTestRetriesEnabled
|
|
1393
|
-
this.options.worldParameters._ddNumTestRetries = numTestRetries
|
|
1394
|
-
|
|
1395
|
-
if (isTestManagementTestsEnabled) {
|
|
1396
|
-
this.options.worldParameters._ddIsTestManagementTestsEnabled = true
|
|
1397
|
-
this.options.worldParameters._ddTestManagementTests = testManagementTests
|
|
1398
|
-
this.options.worldParameters._ddTestManagementAttemptToFixRetries = testManagementAttemptToFixRetries
|
|
1399
|
-
}
|
|
1400
|
-
|
|
1566
|
+
configureParallelWorkerWorldParameters(this.options)
|
|
1401
1567
|
return startWorker.apply(this, args)
|
|
1402
1568
|
})
|
|
1403
1569
|
return adapterPackage
|
|
1404
1570
|
})
|
|
1405
1571
|
|
|
1572
|
+
// Only executed in parallel mode for >=13, in the main process.
|
|
1573
|
+
// Cucumber v13 uses worker_threads and receives worker events via MessagePort.
|
|
1574
|
+
addHook({
|
|
1575
|
+
name: '@cucumber/cucumber',
|
|
1576
|
+
versions: ['>=13.0.0'],
|
|
1577
|
+
file: 'lib/runtime/parallel/adapter.js',
|
|
1578
|
+
}, (adapterPackage) => {
|
|
1579
|
+
shimmer.wrap(
|
|
1580
|
+
adapterPackage.WorkerThreadsAdapter.prototype,
|
|
1581
|
+
'setup',
|
|
1582
|
+
setup => getWrappedWorkerThreadsSetup(setup)
|
|
1583
|
+
)
|
|
1584
|
+
shimmer.wrap(
|
|
1585
|
+
adapterPackage.WorkerThreadsAdapter.prototype,
|
|
1586
|
+
'handleEventFromWorker',
|
|
1587
|
+
handleEventFromWorker => getWrappedHandleWorkerThreadEvent(handleEventFromWorker)
|
|
1588
|
+
)
|
|
1589
|
+
shimmer.wrap(
|
|
1590
|
+
adapterPackage.WorkerThreadsAdapter.prototype,
|
|
1591
|
+
'teardown',
|
|
1592
|
+
teardown => getWrappedWorkerThreadsTeardown(teardown)
|
|
1593
|
+
)
|
|
1594
|
+
return adapterPackage
|
|
1595
|
+
})
|
|
1596
|
+
|
|
1406
1597
|
// Hook executed in the worker process when in parallel mode.
|
|
1407
1598
|
// In this hook we read the information passed in `worldParameters` and make it available for
|
|
1408
1599
|
// `getWrappedRunTestCase`.
|
|
1409
1600
|
addHook({
|
|
1410
1601
|
name: '@cucumber/cucumber',
|
|
1411
|
-
versions: ['>=11.0.0'],
|
|
1602
|
+
versions: ['>=11.0.0 <13.0.0'],
|
|
1412
1603
|
file: 'lib/runtime/parallel/worker.js',
|
|
1413
1604
|
}, (workerPackage) => {
|
|
1414
1605
|
shimmer.wrap(
|
|
@@ -1416,31 +1607,7 @@ addHook({
|
|
|
1416
1607
|
'initialize',
|
|
1417
1608
|
initialize => async function () {
|
|
1418
1609
|
await initialize.apply(this, arguments)
|
|
1419
|
-
|
|
1420
|
-
if (isKnownTestsEnabled) {
|
|
1421
|
-
knownTests = this.options.worldParameters._ddKnownTests
|
|
1422
|
-
// if for whatever reason the worker does not receive valid known tests, we disable EFD and known tests
|
|
1423
|
-
if (!isValidKnownTests(knownTests)) {
|
|
1424
|
-
isKnownTestsEnabled = false
|
|
1425
|
-
knownTests = {}
|
|
1426
|
-
}
|
|
1427
|
-
}
|
|
1428
|
-
isEarlyFlakeDetectionEnabled = !!this.options.worldParameters._ddIsEarlyFlakeDetectionEnabled
|
|
1429
|
-
if (isEarlyFlakeDetectionEnabled) {
|
|
1430
|
-
earlyFlakeDetectionNumRetries = this.options.worldParameters._ddEarlyFlakeDetectionNumRetries
|
|
1431
|
-
earlyFlakeDetectionSlowTestRetries = this.options.worldParameters._ddEarlyFlakeDetectionSlowTestRetries ?? {}
|
|
1432
|
-
}
|
|
1433
|
-
isImpactedTestsEnabled = !!this.options.worldParameters._ddImpactedTestsEnabled
|
|
1434
|
-
if (isImpactedTestsEnabled) {
|
|
1435
|
-
modifiedFiles = this.options.worldParameters._ddModifiedFiles
|
|
1436
|
-
}
|
|
1437
|
-
isFlakyTestRetriesEnabled = !!this.options.worldParameters._ddIsFlakyTestRetriesEnabled
|
|
1438
|
-
numTestRetries = this.options.worldParameters._ddNumTestRetries ?? 0
|
|
1439
|
-
isTestManagementTestsEnabled = !!this.options.worldParameters._ddIsTestManagementTestsEnabled
|
|
1440
|
-
if (isTestManagementTestsEnabled) {
|
|
1441
|
-
testManagementTests = this.options.worldParameters._ddTestManagementTests
|
|
1442
|
-
testManagementAttemptToFixRetries = this.options.worldParameters._ddTestManagementAttemptToFixRetries
|
|
1443
|
-
}
|
|
1610
|
+
readParallelWorkerWorldParameters(this.options)
|
|
1444
1611
|
}
|
|
1445
1612
|
)
|
|
1446
1613
|
return workerPackage
|
|
@@ -25,21 +25,23 @@ addHook({ name: 'express-mongo-sanitize', versions: ['>=1.0.0'] }, expressMongoS
|
|
|
25
25
|
return shimmer.wrapFunction(expressMongoSanitize, expressMongoSanitize => function (...args) {
|
|
26
26
|
const middleware = expressMongoSanitize.apply(this, args)
|
|
27
27
|
|
|
28
|
-
return shimmer.wrapFunction(middleware, middleware => function (
|
|
28
|
+
return shimmer.wrapFunction(middleware, middleware => function (...args) {
|
|
29
29
|
if (!sanitizeMiddlewareFinished.hasSubscribers) {
|
|
30
|
-
return
|
|
30
|
+
return Reflect.apply(middleware, this, args)
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
const
|
|
33
|
+
const req = args[0]
|
|
34
|
+
// Mirror next's name/arity so wrapCallback skips its per-call identity rewrite.
|
|
35
|
+
const wrappedNext = shimmer.wrapCallback(args[2], original => function next (_error) {
|
|
34
36
|
sanitizeMiddlewareFinished.publish({
|
|
35
37
|
sanitizedProperties: propertiesToSanitize,
|
|
36
38
|
req,
|
|
37
39
|
})
|
|
38
40
|
|
|
39
|
-
return
|
|
41
|
+
return original.apply(this, arguments)
|
|
40
42
|
})
|
|
41
43
|
|
|
42
|
-
return middleware.call(this, req,
|
|
44
|
+
return middleware.call(this, req, args[1], wrappedNext)
|
|
43
45
|
})
|
|
44
46
|
})
|
|
45
47
|
})
|
|
@@ -6,22 +6,23 @@ const { channel, addHook } = require('./helpers/instrument')
|
|
|
6
6
|
const sessionMiddlewareFinishCh = channel('datadog:express-session:middleware:finish')
|
|
7
7
|
|
|
8
8
|
function wrapSessionMiddleware (sessionMiddleware) {
|
|
9
|
-
return function wrappedSessionMiddleware (
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
return function wrappedSessionMiddleware (...args) {
|
|
10
|
+
const req = args[0]
|
|
11
|
+
const res = args[1]
|
|
12
|
+
// Mirror next's name/arity so wrapCallback skips its per-call identity rewrite.
|
|
13
|
+
args[2] = shimmer.wrapCallback(args[2], original => function next (_error) {
|
|
14
|
+
if (sessionMiddlewareFinishCh.hasSubscribers) {
|
|
15
|
+
const abortController = new AbortController()
|
|
14
16
|
|
|
15
|
-
|
|
17
|
+
sessionMiddlewareFinishCh.publish({ req, res, sessionId: req.sessionID, abortController })
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return next.apply(this, args)
|
|
19
|
+
if (abortController.signal.aborted) return
|
|
21
20
|
}
|
|
21
|
+
|
|
22
|
+
return original.apply(this, arguments)
|
|
22
23
|
})
|
|
23
24
|
|
|
24
|
-
return
|
|
25
|
+
return Reflect.apply(sessionMiddleware, this, args)
|
|
25
26
|
}
|
|
26
27
|
}
|
|
27
28
|
|