dd-trace 5.97.0 → 5.98.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 +26 -2
- package/package.json +1 -1
- package/packages/datadog-instrumentations/src/cucumber.js +65 -3
- package/packages/datadog-instrumentations/src/cypress-config.js +31 -37
- package/packages/datadog-instrumentations/src/jest.js +104 -12
- package/packages/datadog-instrumentations/src/mocha/utils.js +8 -0
- package/packages/datadog-instrumentations/src/redis.js +12 -6
- package/packages/datadog-plugin-aws-sdk/src/base.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/redshift.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/sns.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -0
- package/packages/datadog-plugin-cucumber/src/index.js +6 -0
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +109 -1
- package/packages/datadog-plugin-cypress/src/index.js +59 -2
- package/packages/datadog-plugin-fs/src/index.js +1 -1
- package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +2 -1
- package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-push-subscription.js +2 -7
- package/packages/datadog-plugin-http/src/client.js +1 -1
- package/packages/datadog-plugin-http/src/server.js +10 -2
- package/packages/datadog-plugin-http2/src/client.js +1 -1
- package/packages/datadog-plugin-http2/src/server.js +10 -2
- package/packages/datadog-plugin-mongodb-core/src/index.js +3 -3
- package/packages/datadog-plugin-mysql/src/index.js +1 -1
- package/packages/datadog-plugin-next/src/index.js +8 -2
- package/packages/datadog-plugin-pg/src/index.js +1 -1
- package/packages/datadog-plugin-tedious/src/index.js +1 -1
- package/packages/datadog-plugin-ws/src/close.js +1 -1
- package/packages/datadog-plugin-ws/src/receiver.js +1 -1
- package/packages/dd-trace/src/aiguard/sdk.js +22 -22
- package/packages/dd-trace/src/appsec/blocked_templates.js +4 -3
- package/packages/dd-trace/src/appsec/blocking.js +62 -34
- package/packages/dd-trace/src/appsec/sdk/set_user.js +1 -1
- package/packages/dd-trace/src/appsec/sdk/track_event.js +5 -5
- package/packages/dd-trace/src/appsec/sdk/user_blocking.js +2 -2
- package/packages/dd-trace/src/appsec/sdk/utils.js +4 -2
- package/packages/dd-trace/src/config/defaults.js +0 -1
- package/packages/dd-trace/src/config/generated-config-types.d.ts +5 -0
- package/packages/dd-trace/src/config/index.js +55 -28
- package/packages/dd-trace/src/config/supported-configurations.json +61 -4
- package/packages/dd-trace/src/constants.js +1 -0
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +5 -2
- package/packages/dd-trace/src/encode/0.4.js +7 -6
- package/packages/dd-trace/src/encode/span-stats.js +4 -1
- package/packages/dd-trace/src/log/index.js +0 -10
- package/packages/dd-trace/src/openfeature/remote_config.js +6 -1
- package/packages/dd-trace/src/opentelemetry/context_manager.js +6 -4
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +17 -2
- package/packages/dd-trace/src/opentelemetry/otlp/protobuf_loader.js +14 -2
- package/packages/dd-trace/src/opentelemetry/otlp/trace.proto +358 -0
- package/packages/dd-trace/src/opentelemetry/otlp/trace_service.proto +78 -0
- package/packages/dd-trace/src/opentelemetry/trace/index.js +75 -0
- package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +66 -0
- package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +332 -0
- package/packages/dd-trace/src/opentracing/tracer.js +9 -4
- package/packages/dd-trace/src/plugins/log_plugin.js +3 -0
- package/packages/dd-trace/src/plugins/plugin.js +6 -11
- package/packages/dd-trace/src/plugins/storage.js +2 -2
- package/packages/dd-trace/src/plugins/tracing.js +22 -5
- package/packages/dd-trace/src/plugins/util/test.js +2 -0
- package/packages/dd-trace/src/plugins/util/web.js +6 -88
- package/packages/dd-trace/src/profiling/profiler.js +34 -77
- package/packages/dd-trace/src/proxy.js +8 -3
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +15 -11
- package/packages/dd-trace/src/service-naming/index.js +1 -1
- package/packages/dd-trace/src/service-naming/schemas/definition.js +4 -1
- package/packages/dd-trace/src/service-naming/schemas/util.js +15 -1
- package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +24 -1
- package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +60 -0
- package/packages/dd-trace/src/service-naming/schemas/v0/web.js +17 -1
- package/packages/dd-trace/src/service-naming/schemas/v0/websocket.js +5 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +17 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/web.js +11 -1
- package/packages/dd-trace/src/service-naming/schemas/v1/websocket.js +6 -0
- package/packages/dd-trace/src/span_stats.js +5 -1
- package/packages/dd-trace/src/tracer.js +2 -2
- package/vendor/dist/@apm-js-collab/code-transformer/index.js +28 -6
- package/vendor/dist/protobufjs/index.js +1 -1
- package/packages/dd-trace/src/log/utils.js +0 -16
package/index.d.ts
CHANGED
|
@@ -665,7 +665,15 @@ declare namespace tracer {
|
|
|
665
665
|
* @env DD_RUNTIME_METRICS_EVENT_LOOP_ENABLED
|
|
666
666
|
* Programmatic configuration takes precedence over the environment variables listed above.
|
|
667
667
|
*/
|
|
668
|
-
eventLoop?: boolean
|
|
668
|
+
eventLoop?: boolean,
|
|
669
|
+
|
|
670
|
+
/**
|
|
671
|
+
* Whether to use native metrics. When set to false, forces the JS implementation
|
|
672
|
+
* @default true
|
|
673
|
+
* @env DD_RUNTIME_METRICS_NATIVE
|
|
674
|
+
* Programmatic configuration takes precedence over the environment variables listed above.
|
|
675
|
+
*/
|
|
676
|
+
native?: boolean
|
|
669
677
|
}
|
|
670
678
|
|
|
671
679
|
/**
|
|
@@ -795,7 +803,7 @@ declare namespace tracer {
|
|
|
795
803
|
* Whether to request blocking mode when evaluating prompts via auto-instrumentation.
|
|
796
804
|
* When `true`, AI Guard will block requests that violate security policies.
|
|
797
805
|
* When `false`, AI Guard evaluates but never blocks (monitor-only mode).
|
|
798
|
-
* @default
|
|
806
|
+
* @default true
|
|
799
807
|
* @env DD_AI_GUARD_BLOCK
|
|
800
808
|
* Programmatic configuration takes precedence over the environment variables listed above.
|
|
801
809
|
*/
|
|
@@ -1847,6 +1855,10 @@ declare namespace tracer {
|
|
|
1847
1855
|
* List of tags associated with the evaluation (e.g. indirect-prompt-injection)
|
|
1848
1856
|
*/
|
|
1849
1857
|
tags: string[];
|
|
1858
|
+
/**
|
|
1859
|
+
* Dictionary of tag probabilities (e.g. { indirect-prompt-injection: 0.2, jailbreak-attempt: 0.8 })
|
|
1860
|
+
*/
|
|
1861
|
+
tagProbabilities: { [key: string]: number }
|
|
1850
1862
|
/**
|
|
1851
1863
|
* Sensitive Data Scanner findings from the evaluation.
|
|
1852
1864
|
*/
|
|
@@ -1866,6 +1878,10 @@ declare namespace tracer {
|
|
|
1866
1878
|
* List of tags associated with the evaluation (e.g. indirect-prompt-injection)
|
|
1867
1879
|
*/
|
|
1868
1880
|
tags: string[];
|
|
1881
|
+
/**
|
|
1882
|
+
* Dictionary of tag probabilities (e.g. { indirect-prompt-injection: 0.2, jailbreak-attempt: 0.8 })
|
|
1883
|
+
*/
|
|
1884
|
+
tagProbabilities: { [key: string]: number }
|
|
1869
1885
|
/**
|
|
1870
1886
|
* Sensitive Data Scanner findings from the evaluation.
|
|
1871
1887
|
*/
|
|
@@ -3048,6 +3064,14 @@ declare namespace tracer {
|
|
|
3048
3064
|
* @returns true to instrument the command, false to skip it
|
|
3049
3065
|
*/
|
|
3050
3066
|
filter?: (command: string) => boolean;
|
|
3067
|
+
|
|
3068
|
+
/**
|
|
3069
|
+
* Whether to use a different service name for each Redis instance based
|
|
3070
|
+
* on the configured connection name of the client.
|
|
3071
|
+
*
|
|
3072
|
+
* @default false
|
|
3073
|
+
*/
|
|
3074
|
+
splitByInstance?: boolean;
|
|
3051
3075
|
}
|
|
3052
3076
|
|
|
3053
3077
|
/**
|
package/package.json
CHANGED
|
@@ -235,6 +235,40 @@ function getPickleByFile (runtimeOrCoodinator) {
|
|
|
235
235
|
}, {})
|
|
236
236
|
}
|
|
237
237
|
|
|
238
|
+
function getFinalStatus ({
|
|
239
|
+
status,
|
|
240
|
+
hasFailedAllRetries,
|
|
241
|
+
isLastAtrRetry,
|
|
242
|
+
isLastEfdRetry,
|
|
243
|
+
isLastAttemptToFix,
|
|
244
|
+
hasPassedAllRetries,
|
|
245
|
+
isQuarantined,
|
|
246
|
+
isDisabled,
|
|
247
|
+
}) {
|
|
248
|
+
// Note that intermediate executions DO NOT report a final status tag
|
|
249
|
+
|
|
250
|
+
// If the test is quarantined or disabled, regardless of its actual execution result or active retry features,
|
|
251
|
+
// the final status of its last execution should be reported as 'skip'.
|
|
252
|
+
if (isQuarantined || isDisabled || status === 'skip') {
|
|
253
|
+
return 'skip'
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// When no retry feature is active, every execution is final
|
|
257
|
+
if (!isLastAtrRetry && !isLastEfdRetry && !isLastAttemptToFix) {
|
|
258
|
+
return status
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// ATR and EFD: pass unless every attempt failed
|
|
262
|
+
if (isLastAtrRetry || isLastEfdRetry) {
|
|
263
|
+
return hasFailedAllRetries ? 'fail' : 'pass'
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Branch for ATF (We need to check hasPassedAllRetries)
|
|
267
|
+
if (isLastAttemptToFix) {
|
|
268
|
+
return hasPassedAllRetries ? 'pass' : 'fail'
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
238
272
|
function wrapRun (pl, isLatestVersion, version) {
|
|
239
273
|
if (patched.has(pl)) return
|
|
240
274
|
|
|
@@ -262,7 +296,7 @@ function wrapRun (pl, isLatestVersion, version) {
|
|
|
262
296
|
testStartCh.runStores(ctx, () => {})
|
|
263
297
|
const promises = {}
|
|
264
298
|
try {
|
|
265
|
-
|
|
299
|
+
const onEnvelope = async (testCase) => {
|
|
266
300
|
// Only supported from >=8.0.0
|
|
267
301
|
if (testCase?.testCaseFinished) {
|
|
268
302
|
const { testCaseFinished: { willBeRetried } } = testCase
|
|
@@ -280,7 +314,7 @@ function wrapRun (pl, isLatestVersion, version) {
|
|
|
280
314
|
const isAtrRetry = !isFirstAttempt && isFlakyTestRetriesEnabled
|
|
281
315
|
|
|
282
316
|
// ATR: record this attempt as failed so when run().finally runs (after retry) we have all statuses
|
|
283
|
-
if (isFlakyTestRetriesEnabled
|
|
317
|
+
if (isFlakyTestRetriesEnabled) {
|
|
284
318
|
const nameForKey = this.pickle.name.replace(/\s*\(attempt \d+(?:, retried)?\)\s*$/, '')
|
|
285
319
|
const atrKey = `${this.pickle.uri}:${nameForKey}`
|
|
286
320
|
if (atrStatusesByScenarioKey.has(atrKey)) {
|
|
@@ -303,13 +337,15 @@ function wrapRun (pl, isLatestVersion, version) {
|
|
|
303
337
|
testStartCh.runStores(newCtx, () => {})
|
|
304
338
|
}
|
|
305
339
|
}
|
|
306
|
-
}
|
|
340
|
+
}
|
|
341
|
+
this.eventBroadcaster.on('envelope', onEnvelope)
|
|
307
342
|
let promise
|
|
308
343
|
|
|
309
344
|
testFnCh.runStores(ctx, () => {
|
|
310
345
|
promise = run.apply(this, arguments)
|
|
311
346
|
})
|
|
312
347
|
promise.finally(async () => {
|
|
348
|
+
this.eventBroadcaster.removeListener('envelope', onEnvelope)
|
|
313
349
|
const result = this.getWorstStepResult()
|
|
314
350
|
const { status, skipReason } = isLatestVersion
|
|
315
351
|
? getStatusFromResultLatest(result)
|
|
@@ -408,6 +444,31 @@ function wrapRun (pl, isLatestVersion, version) {
|
|
|
408
444
|
if (promises.hitBreakpointPromise) {
|
|
409
445
|
await promises.hitBreakpointPromise
|
|
410
446
|
}
|
|
447
|
+
|
|
448
|
+
// Notice that ATR is handled using cucumber native retries features.
|
|
449
|
+
// Therefore, if we reach this point, we are certain that it's the last ATR execution
|
|
450
|
+
const isLastAtrRetry = isFlakyTestRetriesEnabled && !isAttemptToFix && !isEfdRetry && numTestRetries > 0
|
|
451
|
+
|
|
452
|
+
const statuses = lastStatusByPickleId.get(this.pickle.id)
|
|
453
|
+
const isLastEfdRetry = isEfdRetry && statuses?.length === earlyFlakeDetectionNumRetries + 1
|
|
454
|
+
const isLastAttemptToFixRetry = isAttemptToFix && statuses?.length === testManagementAttemptToFixRetries + 1
|
|
455
|
+
|
|
456
|
+
// Intermediate (non-last EFD or ATF retries) executions do not report a final status
|
|
457
|
+
const isIntermediateExecution = (isEfdRetry && !isLastEfdRetry) || (isAttemptToFix && !isLastAttemptToFixRetry)
|
|
458
|
+
|
|
459
|
+
const finalStatus = isIntermediateExecution
|
|
460
|
+
? undefined
|
|
461
|
+
: getFinalStatus({
|
|
462
|
+
status,
|
|
463
|
+
hasFailedAllRetries,
|
|
464
|
+
isLastAtrRetry,
|
|
465
|
+
isLastEfdRetry,
|
|
466
|
+
isLastAttemptToFix: isLastAttemptToFixRetry,
|
|
467
|
+
hasPassedAllRetries,
|
|
468
|
+
isQuarantined,
|
|
469
|
+
isDisabled,
|
|
470
|
+
})
|
|
471
|
+
|
|
411
472
|
testFinishCh.publish({
|
|
412
473
|
status,
|
|
413
474
|
skipReason,
|
|
@@ -424,6 +485,7 @@ function wrapRun (pl, isLatestVersion, version) {
|
|
|
424
485
|
isQuarantined,
|
|
425
486
|
isModified,
|
|
426
487
|
...attemptCtx.currentStore,
|
|
488
|
+
finalStatus,
|
|
427
489
|
})
|
|
428
490
|
})
|
|
429
491
|
return promise
|
|
@@ -4,9 +4,21 @@ const fs = require('fs')
|
|
|
4
4
|
const os = require('os')
|
|
5
5
|
const path = require('path')
|
|
6
6
|
const { pathToFileURL } = require('url')
|
|
7
|
+
const { channel } = require('./helpers/instrument')
|
|
7
8
|
|
|
8
9
|
const DD_CONFIG_WRAPPED = Symbol('dd-trace.cypress.config.wrapped')
|
|
9
10
|
|
|
11
|
+
const setupNodeEventsCh = channel('ci:cypress:setup-node-events')
|
|
12
|
+
|
|
13
|
+
// Ensure the cypress plugin is loaded so it can subscribe to our channel.
|
|
14
|
+
// Normally, plugins are loaded when their npm module is required (via addHook),
|
|
15
|
+
// but plain-object configs don't require('cypress'), so the plugin would never
|
|
16
|
+
// be instantiated in the Cypress Config Manager child process.
|
|
17
|
+
const loadCh = channel('dd-trace:instrumentation:load')
|
|
18
|
+
if (loadCh.hasSubscribers) {
|
|
19
|
+
loadCh.publish({ name: 'cypress' })
|
|
20
|
+
}
|
|
21
|
+
|
|
10
22
|
const noopTask = {
|
|
11
23
|
'dd:testSuiteStart': () => null,
|
|
12
24
|
'dd:beforeEach': () => ({}),
|
|
@@ -93,8 +105,9 @@ function injectSupportFile (config) {
|
|
|
93
105
|
|
|
94
106
|
/**
|
|
95
107
|
* Registers dd-trace's Cypress hooks (before:run, after:spec, after:run, tasks)
|
|
96
|
-
* and injects the support file.
|
|
97
|
-
*
|
|
108
|
+
* and injects the support file. Communicates with the plugin layer via
|
|
109
|
+
* the `ci:cypress:setup-node-events` diagnostic channel, avoiding direct
|
|
110
|
+
* tracer references in the instrumentation layer.
|
|
98
111
|
*
|
|
99
112
|
* @param {Function} on Cypress event registration function
|
|
100
113
|
* @param {object} config Cypress resolved config object
|
|
@@ -111,8 +124,6 @@ function registerDdTraceHooks (on, config, userAfterSpecHandlers, userAfterRunHa
|
|
|
111
124
|
}
|
|
112
125
|
}
|
|
113
126
|
|
|
114
|
-
const tracer = global._ddtrace
|
|
115
|
-
|
|
116
127
|
const registerAfterRunWithCleanup = () => {
|
|
117
128
|
on('after:run', (results) => {
|
|
118
129
|
const chain = userAfterRunHandlers.reduce(
|
|
@@ -129,49 +140,32 @@ function registerDdTraceHooks (on, config, userAfterSpecHandlers, userAfterRunHa
|
|
|
129
140
|
on('task', noopTask)
|
|
130
141
|
}
|
|
131
142
|
|
|
132
|
-
if (!
|
|
143
|
+
if (!setupNodeEventsCh.hasSubscribers) {
|
|
133
144
|
registerNoopHandlers()
|
|
134
145
|
return config
|
|
135
146
|
}
|
|
136
147
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
148
|
+
// Publish to the plugin layer via diagnostic channel.
|
|
149
|
+
// The subscriber sets `payload.registered = true` and optionally
|
|
150
|
+
// `payload.configPromise` when it handles the event.
|
|
151
|
+
const payload = {
|
|
152
|
+
on,
|
|
153
|
+
config,
|
|
154
|
+
userAfterSpecHandlers,
|
|
155
|
+
userAfterRunHandlers,
|
|
156
|
+
cleanupWrapper,
|
|
157
|
+
registered: false,
|
|
158
|
+
configPromise: undefined,
|
|
142
159
|
}
|
|
143
160
|
|
|
144
|
-
|
|
161
|
+
setupNodeEventsCh.publish(payload)
|
|
145
162
|
|
|
146
|
-
if (
|
|
147
|
-
|
|
148
|
-
registerAfterRunWithCleanup()
|
|
163
|
+
if (!payload.registered) {
|
|
164
|
+
registerNoopHandlers()
|
|
149
165
|
return config
|
|
150
166
|
}
|
|
151
167
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
on('after:spec', (spec, results) => {
|
|
155
|
-
const chain = userAfterSpecHandlers.reduce(
|
|
156
|
-
(p, h) => p.then(() => h(spec, results)),
|
|
157
|
-
Promise.resolve()
|
|
158
|
-
)
|
|
159
|
-
return chain.then(() => cypressPlugin.afterSpec(spec, results))
|
|
160
|
-
})
|
|
161
|
-
|
|
162
|
-
on('after:run', (results) => {
|
|
163
|
-
const chain = userAfterRunHandlers.reduce(
|
|
164
|
-
(p, h) => p.then(() => h(results)),
|
|
165
|
-
Promise.resolve()
|
|
166
|
-
)
|
|
167
|
-
return chain
|
|
168
|
-
.then(() => cypressPlugin.afterRun(results))
|
|
169
|
-
.finally(cleanupWrapper)
|
|
170
|
-
})
|
|
171
|
-
|
|
172
|
-
on('task', cypressPlugin.getTasks())
|
|
173
|
-
|
|
174
|
-
return Promise.resolve(cypressPlugin.init(tracer, config)).then(() => config)
|
|
168
|
+
return payload.configPromise || config
|
|
175
169
|
}
|
|
176
170
|
|
|
177
171
|
/**
|
|
@@ -11,6 +11,7 @@ const {
|
|
|
11
11
|
JEST_WORKER_TRACE_PAYLOAD_CODE,
|
|
12
12
|
JEST_WORKER_COVERAGE_PAYLOAD_CODE,
|
|
13
13
|
JEST_WORKER_TELEMETRY_PAYLOAD_CODE,
|
|
14
|
+
JEST_WORKER_QUARANTINE_PAYLOAD_CODE,
|
|
14
15
|
getTestLineStart,
|
|
15
16
|
getTestSuitePath,
|
|
16
17
|
getTestParametersString,
|
|
@@ -123,6 +124,38 @@ const BREAKPOINT_HIT_GRACE_PERIOD_MS = 200
|
|
|
123
124
|
const ATR_RETRY_SUPPRESSION_FLAG = '_ddDisableAtrRetry'
|
|
124
125
|
const atrSuppressedErrors = new Map()
|
|
125
126
|
|
|
127
|
+
// Track quarantined tests whose errors were suppressed, keyed by "suite › testName"
|
|
128
|
+
const quarantinedFailingTests = new Set()
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Sends suppressed quarantine test names from a worker process to the main process.
|
|
132
|
+
* Supports both child_process (process.send) and worker_threads (parentPort.postMessage).
|
|
133
|
+
* Returns true if the data was sent (worker mode), false if in main process (runInBand).
|
|
134
|
+
*
|
|
135
|
+
* @param {string[]} testNames
|
|
136
|
+
* @returns {boolean}
|
|
137
|
+
*/
|
|
138
|
+
function sendQuarantineInfoToMainProcess (testNames) {
|
|
139
|
+
const payload = [JEST_WORKER_QUARANTINE_PAYLOAD_CODE, JSON.stringify(testNames)]
|
|
140
|
+
|
|
141
|
+
if (process.send) {
|
|
142
|
+
process.send(payload)
|
|
143
|
+
return true
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
try {
|
|
147
|
+
const { isMainThread, parentPort } = require('node:worker_threads')
|
|
148
|
+
if (!isMainThread && parentPort) {
|
|
149
|
+
parentPort.postMessage(payload)
|
|
150
|
+
return true
|
|
151
|
+
}
|
|
152
|
+
} catch {
|
|
153
|
+
// Not in a worker context
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return false
|
|
157
|
+
}
|
|
158
|
+
|
|
126
159
|
// based on https://github.com/facebook/jest/blob/main/packages/jest-circus/src/formatNodeAssertErrors.ts#L41
|
|
127
160
|
function formatJestError (errors) {
|
|
128
161
|
let error
|
|
@@ -756,6 +789,18 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
|
|
|
756
789
|
const willBeRetriedByFailedTestReplay = numRetries > 0 && numTestExecutions - 1 < numRetries
|
|
757
790
|
const mightHitBreakpoint = this.isDiEnabled && numTestExecutions >= 2
|
|
758
791
|
|
|
792
|
+
// For quarantined tests, suppress errors so Jest doesn't count them as failures.
|
|
793
|
+
// This prevents --bail from stopping the test run on quarantined test failures.
|
|
794
|
+
// The actual status ('fail') is already captured above for dd-trace reporting.
|
|
795
|
+
// Only suppress on the final execution — not when ATR/EFD/ATF will retry the test.
|
|
796
|
+
if (!event.test?.[ATR_RETRY_SUPPRESSION_FLAG] && !willBeRetriedByFailedTestReplay) {
|
|
797
|
+
const quarantineCtx = testContexts.get(event.test)
|
|
798
|
+
if (quarantineCtx?.isQuarantined && event.test.errors?.length) {
|
|
799
|
+
quarantinedFailingTests.add(`${quarantineCtx.suite} › ${quarantineCtx.name}`)
|
|
800
|
+
event.test.errors = []
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
|
|
759
804
|
const ctx = testContexts.get(event.test)
|
|
760
805
|
if (!ctx) {
|
|
761
806
|
log.warn('"ci:jest:test_done": no context found for test "%s"', testName)
|
|
@@ -813,9 +858,25 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
|
|
|
813
858
|
}
|
|
814
859
|
if (event.name === 'run_finish') {
|
|
815
860
|
for (const [test, errors] of atrSuppressedErrors) {
|
|
816
|
-
|
|
861
|
+
// Do not restore errors for quarantined tests — they should stay suppressed
|
|
862
|
+
// so Jest doesn't see the failure (prevents --bail from stopping the run).
|
|
863
|
+
const ctx = testContexts.get(test)
|
|
864
|
+
if (ctx?.isQuarantined) {
|
|
865
|
+
const testName = getJestTestName(test, this.getShouldStripSeedFromTestName())
|
|
866
|
+
quarantinedFailingTests.add(`${ctx.suite} › ${testName}`)
|
|
867
|
+
} else {
|
|
868
|
+
test.errors = errors
|
|
869
|
+
}
|
|
817
870
|
}
|
|
818
871
|
atrSuppressedErrors.clear()
|
|
872
|
+
|
|
873
|
+
// In parallel mode, send suppressed quarantine info to the main process
|
|
874
|
+
// so it can include them in the session summary.
|
|
875
|
+
// In runInBand mode, keep the set — it will be consumed by the session-level code directly.
|
|
876
|
+
if (quarantinedFailingTests.size > 0 && sendQuarantineInfoToMainProcess([...quarantinedFailingTests])) {
|
|
877
|
+
quarantinedFailingTests.clear()
|
|
878
|
+
}
|
|
879
|
+
|
|
819
880
|
efdDeterminedRetries.clear()
|
|
820
881
|
efdSlowAbortedTests.clear()
|
|
821
882
|
efdNewTestCandidates.clear()
|
|
@@ -1255,6 +1316,7 @@ function getCliWrapper (isNewJestVersion) {
|
|
|
1255
1316
|
|
|
1256
1317
|
let numFailedQuarantinedTests = 0
|
|
1257
1318
|
let numFailedQuarantinedOrDisabledAttemptedToFixTests = 0
|
|
1319
|
+
let numSuppressedQuarantinedTests = 0
|
|
1258
1320
|
if (isTestManagementTestsEnabled) {
|
|
1259
1321
|
const failedTests = result
|
|
1260
1322
|
.results
|
|
@@ -1291,45 +1353,69 @@ function getCliWrapper (isNewJestVersion) {
|
|
|
1291
1353
|
}
|
|
1292
1354
|
}
|
|
1293
1355
|
|
|
1356
|
+
// Include quarantined tests whose errors were suppressed at test_done time.
|
|
1357
|
+
// These tests don't appear as failed in Jest's results because their errors were cleared
|
|
1358
|
+
// to prevent --bail from stopping the run, but they should still be counted for the summary.
|
|
1359
|
+
for (const name of quarantinedFailingTests) {
|
|
1360
|
+
if (!quarantineIgnoredNames.includes(name)) {
|
|
1361
|
+
numSuppressedQuarantinedTests++
|
|
1362
|
+
quarantineIgnoredNames.push(name)
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
quarantinedFailingTests.clear()
|
|
1366
|
+
|
|
1294
1367
|
// If every test that failed was quarantined, we'll consider the suite passed
|
|
1295
1368
|
// Note that if a test is attempted to fix,
|
|
1296
1369
|
// it's considered quarantined both if it's disabled and if it's quarantined
|
|
1297
1370
|
// (it'll run but its status is ignored)
|
|
1298
1371
|
// Skip if EFD block already flipped (to avoid logging twice)
|
|
1372
|
+
// Only use visible failures (from Jest results) for the flip check.
|
|
1373
|
+
// Suppressed quarantine failures are not in numFailedTests.
|
|
1374
|
+
const visibleQuarantineFailures = numFailedQuarantinedTests + numFailedQuarantinedOrDisabledAttemptedToFixTests
|
|
1299
1375
|
if (
|
|
1300
1376
|
!result.results.success &&
|
|
1301
1377
|
!mustNotFlipSuccess &&
|
|
1302
|
-
|
|
1303
|
-
result.results.numFailedTests ===
|
|
1304
|
-
numFailedQuarantinedTests + numFailedQuarantinedOrDisabledAttemptedToFixTests
|
|
1378
|
+
visibleQuarantineFailures !== 0 &&
|
|
1379
|
+
result.results.numFailedTests === visibleQuarantineFailures
|
|
1305
1380
|
) {
|
|
1306
1381
|
result.results.success = true
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
const totalQuarantineFailures = visibleQuarantineFailures + numSuppressedQuarantinedTests
|
|
1385
|
+
if (totalQuarantineFailures > 0) {
|
|
1386
|
+
if (ignoredFailuresSummary) {
|
|
1387
|
+
ignoredFailuresSummary.quarantineNames = quarantineIgnoredNames
|
|
1388
|
+
ignoredFailuresSummary.totalCount += totalQuarantineFailures
|
|
1389
|
+
} else {
|
|
1390
|
+
ignoredFailuresSummary = {
|
|
1391
|
+
efdNames: [],
|
|
1392
|
+
quarantineNames: quarantineIgnoredNames,
|
|
1393
|
+
totalCount: totalQuarantineFailures,
|
|
1394
|
+
}
|
|
1311
1395
|
}
|
|
1312
1396
|
}
|
|
1313
1397
|
}
|
|
1314
1398
|
|
|
1315
1399
|
// Combined check: if all failed tests are accounted for by EFD (flaky retries) and/or quarantine,
|
|
1316
1400
|
// we should consider the suite passed even when neither check alone covers all failures.
|
|
1401
|
+
// Only visible failures (in Jest results) are compared — suppressed quarantine failures
|
|
1402
|
+
// are already removed from numFailedTests at test_done time.
|
|
1317
1403
|
if (
|
|
1318
1404
|
!result.results.success &&
|
|
1319
1405
|
!mustNotFlipSuccess &&
|
|
1320
1406
|
(isEarlyFlakeDetectionEnabled || isTestManagementTestsEnabled)
|
|
1321
1407
|
) {
|
|
1322
|
-
const
|
|
1408
|
+
const visibleIgnoredFailures =
|
|
1323
1409
|
numEfdFailedTestsToIgnore + numFailedQuarantinedTests + numFailedQuarantinedOrDisabledAttemptedToFixTests
|
|
1324
1410
|
if (
|
|
1325
|
-
|
|
1326
|
-
result.results.numFailedTests ===
|
|
1411
|
+
visibleIgnoredFailures !== 0 &&
|
|
1412
|
+
result.results.numFailedTests === visibleIgnoredFailures
|
|
1327
1413
|
) {
|
|
1328
1414
|
result.results.success = true
|
|
1329
1415
|
ignoredFailuresSummary = {
|
|
1330
1416
|
efdNames: efdIgnoredNames,
|
|
1331
1417
|
quarantineNames: quarantineIgnoredNames,
|
|
1332
|
-
totalCount:
|
|
1418
|
+
totalCount: visibleIgnoredFailures + numSuppressedQuarantinedTests,
|
|
1333
1419
|
}
|
|
1334
1420
|
}
|
|
1335
1421
|
}
|
|
@@ -1865,6 +1951,12 @@ function onMessageWrapper (onMessage) {
|
|
|
1865
1951
|
workerReportTelemetryCh.publish(data)
|
|
1866
1952
|
return
|
|
1867
1953
|
}
|
|
1954
|
+
if (code === JEST_WORKER_QUARANTINE_PAYLOAD_CODE) { // quarantined test failures suppressed in worker
|
|
1955
|
+
for (const name of JSON.parse(data)) {
|
|
1956
|
+
quarantinedFailingTests.add(name)
|
|
1957
|
+
}
|
|
1958
|
+
return
|
|
1959
|
+
}
|
|
1868
1960
|
return onMessage.apply(this, arguments)
|
|
1869
1961
|
}
|
|
1870
1962
|
}
|
|
@@ -264,6 +264,14 @@ function getFinalStatus ({
|
|
|
264
264
|
}) {
|
|
265
265
|
// Note that intermediate executions DO NOT report a final status tag
|
|
266
266
|
|
|
267
|
+
// Intermediate EFD and ATF executions must not carry a final status, regardless of quarantine/disabled state
|
|
268
|
+
const isIntermediateExecution =
|
|
269
|
+
(isEfdRetry && !isLastEfdRetry) ||
|
|
270
|
+
(isAttemptToFix && !isLastAttemptToFix)
|
|
271
|
+
if (isIntermediateExecution) {
|
|
272
|
+
return
|
|
273
|
+
}
|
|
274
|
+
|
|
267
275
|
// If the test is quarantined or disabled, regardless of its actual execution result or active retry features,
|
|
268
276
|
// the final status of its last execution should be reported as 'skip'.
|
|
269
277
|
if (isQuarantined || isDisabled) {
|
|
@@ -11,6 +11,8 @@ const finishCh = channel('apm:redis:command:finish')
|
|
|
11
11
|
const errorCh = channel('apm:redis:command:error')
|
|
12
12
|
|
|
13
13
|
let createClientUrl
|
|
14
|
+
let createClientName
|
|
15
|
+
const instanceInfo = new WeakMap()
|
|
14
16
|
|
|
15
17
|
function wrapAddCommand (addCommand) {
|
|
16
18
|
return function (command) {
|
|
@@ -21,7 +23,7 @@ function wrapAddCommand (addCommand) {
|
|
|
21
23
|
const name = command[0]
|
|
22
24
|
const args = command.slice(1)
|
|
23
25
|
|
|
24
|
-
const ctx = getStartCtx(this, name, args
|
|
26
|
+
const ctx = getStartCtx(this, name, args)
|
|
25
27
|
return startCh.runStores(ctx, () => {
|
|
26
28
|
const res = addCommand.apply(this, arguments)
|
|
27
29
|
|
|
@@ -36,17 +38,16 @@ function wrapCommandQueueClass (cls) {
|
|
|
36
38
|
const ret = class RedisCommandQueue extends cls {
|
|
37
39
|
constructor (...args) {
|
|
38
40
|
super(...args)
|
|
41
|
+
let url = { host: 'localhost', port: 6379 }
|
|
39
42
|
if (createClientUrl) {
|
|
40
43
|
try {
|
|
41
44
|
const parsed = new URL(createClientUrl)
|
|
42
|
-
|
|
43
|
-
this._url = { host: parsed.hostname, port: Number(parsed.port) || 6379 }
|
|
44
|
-
}
|
|
45
|
+
url = { host: parsed.hostname, port: Number(parsed.port) || 6379 }
|
|
45
46
|
} catch {
|
|
46
47
|
// ignore
|
|
47
48
|
}
|
|
48
49
|
}
|
|
49
|
-
|
|
50
|
+
instanceInfo.set(this, { connectionName: createClientName, url })
|
|
50
51
|
}
|
|
51
52
|
}
|
|
52
53
|
return ret
|
|
@@ -55,8 +56,10 @@ function wrapCommandQueueClass (cls) {
|
|
|
55
56
|
function wrapCreateClient (request) {
|
|
56
57
|
return function (opts) {
|
|
57
58
|
createClientUrl = opts && opts.url
|
|
59
|
+
createClientName = opts && opts.name
|
|
58
60
|
const ret = request.apply(this, arguments)
|
|
59
61
|
createClientUrl = undefined
|
|
62
|
+
createClientName = undefined
|
|
60
63
|
return ret
|
|
61
64
|
}
|
|
62
65
|
}
|
|
@@ -134,12 +137,15 @@ addHook({ name: 'redis', versions: ['>=0.12 <2.6'] }, redis => {
|
|
|
134
137
|
return redis
|
|
135
138
|
})
|
|
136
139
|
|
|
137
|
-
function getStartCtx (client, command, args
|
|
140
|
+
function getStartCtx (client, command, args) {
|
|
141
|
+
const { url, connectionName } = instanceInfo.get(client) || {}
|
|
142
|
+
|
|
138
143
|
return {
|
|
139
144
|
db: client.selected_db,
|
|
140
145
|
command,
|
|
141
146
|
args,
|
|
142
147
|
connectionOptions: client.connection_options || client.connection_option || client.connectionOption || url,
|
|
148
|
+
connectionName,
|
|
143
149
|
}
|
|
144
150
|
}
|
|
145
151
|
|
|
@@ -56,7 +56,6 @@ class BaseAwsSdkPlugin extends ClientPlugin {
|
|
|
56
56
|
|
|
57
57
|
const meta = {
|
|
58
58
|
'span.kind': 'client',
|
|
59
|
-
'service.name': this.serviceName(),
|
|
60
59
|
'aws.operation': operation,
|
|
61
60
|
'aws.region': awsRegion,
|
|
62
61
|
region: awsRegion,
|
|
@@ -70,6 +69,7 @@ class BaseAwsSdkPlugin extends ClientPlugin {
|
|
|
70
69
|
const span = this.startSpan(this.operationFromRequest(request), {
|
|
71
70
|
childOf,
|
|
72
71
|
meta,
|
|
72
|
+
service: this.serviceName(),
|
|
73
73
|
integrationName: 'aws-sdk',
|
|
74
74
|
}, ctx)
|
|
75
75
|
|
|
@@ -12,6 +12,7 @@ class EventBridge extends BaseAwsSdkPlugin {
|
|
|
12
12
|
return {
|
|
13
13
|
'resource.name': operation ? `${operation} ${params.source}` : params.source,
|
|
14
14
|
'aws.eventbridge.source': `${params.source}`,
|
|
15
|
+
'messaging.system': 'aws_eventbridge',
|
|
15
16
|
rulename: `${rulename}`,
|
|
16
17
|
}
|
|
17
18
|
}
|
|
@@ -11,6 +11,7 @@ class Redshift extends BaseAwsSdkPlugin {
|
|
|
11
11
|
return {
|
|
12
12
|
'resource.name': `${operation} ${params.ClusterIdentifier}`,
|
|
13
13
|
'aws.redshift.cluster_identifier': params.ClusterIdentifier,
|
|
14
|
+
'db.system': 'aws.redshift',
|
|
14
15
|
clusteridentifier: params.ClusterIdentifier,
|
|
15
16
|
}
|
|
16
17
|
}
|
|
@@ -100,6 +100,7 @@ class Sqs extends BaseAwsSdkPlugin {
|
|
|
100
100
|
const tags = {
|
|
101
101
|
'resource.name': `${operation} ${params.QueueName || params.QueueUrl}`,
|
|
102
102
|
'aws.sqs.queue_name': params.QueueName || params.QueueUrl,
|
|
103
|
+
'messaging.system': 'aws_sqs',
|
|
103
104
|
queuename: queueName,
|
|
104
105
|
}
|
|
105
106
|
|
|
@@ -37,6 +37,7 @@ const {
|
|
|
37
37
|
TEST_SOURCE_FILE,
|
|
38
38
|
TEST_SOURCE_START,
|
|
39
39
|
TEST_STATUS,
|
|
40
|
+
TEST_FINAL_STATUS,
|
|
40
41
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
41
42
|
const { RESOURCE_NAME } = require('../../../ext/tags')
|
|
42
43
|
const { COMPONENT, ERROR_MESSAGE } = require('../../dd-trace/src/constants')
|
|
@@ -307,11 +308,16 @@ class CucumberPlugin extends CiPlugin {
|
|
|
307
308
|
isDisabled,
|
|
308
309
|
isQuarantined,
|
|
309
310
|
isModified,
|
|
311
|
+
finalStatus,
|
|
310
312
|
}) => {
|
|
311
313
|
const statusTag = isStep ? 'step.status' : TEST_STATUS
|
|
312
314
|
|
|
313
315
|
span.setTag(statusTag, status)
|
|
314
316
|
|
|
317
|
+
if (finalStatus) {
|
|
318
|
+
span.setTag(TEST_FINAL_STATUS, finalStatus)
|
|
319
|
+
}
|
|
320
|
+
|
|
315
321
|
if (isNew) {
|
|
316
322
|
span.setTag(TEST_IS_NEW, 'true')
|
|
317
323
|
if (isEfdRetry) {
|