dd-trace 5.110.0 → 5.112.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE-3rdparty.csv +1 -2
- package/README.md +13 -8
- package/ci/init.js +21 -2
- package/ci/vitest-no-worker-init-setup.mjs +430 -0
- package/ext/tags.js +2 -0
- package/index.d.ts +34 -1
- package/initialize.mjs +5 -6
- package/loader-hook.mjs +76 -55
- package/package.json +37 -34
- package/packages/datadog-instrumentations/src/ai.js +45 -0
- package/packages/datadog-instrumentations/src/apollo-server.js +5 -13
- package/packages/datadog-instrumentations/src/child_process.js +3 -3
- package/packages/datadog-instrumentations/src/claude-agent-sdk.js +587 -0
- package/packages/datadog-instrumentations/src/cucumber.js +102 -43
- package/packages/datadog-instrumentations/src/cypress-config.js +11 -3
- package/packages/datadog-instrumentations/src/fastify.js +27 -8
- package/packages/datadog-instrumentations/src/fs.js +8 -6
- package/packages/datadog-instrumentations/src/graphql.js +26 -495
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/instrument.js +7 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/ai.js +6 -6
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/claude-agent-sdk.js +17 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql.js +201 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +2 -0
- package/packages/datadog-instrumentations/src/jest.js +713 -63
- package/packages/datadog-instrumentations/src/mocha/main.js +24 -3
- package/packages/datadog-instrumentations/src/mocha/utils.js +128 -22
- package/packages/datadog-instrumentations/src/mocha/worker.js +13 -0
- package/packages/datadog-instrumentations/src/mongodb.js +74 -0
- package/packages/datadog-instrumentations/src/mongoose.js +13 -2
- package/packages/datadog-instrumentations/src/playwright.js +13 -9
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +1297 -0
- package/packages/datadog-instrumentations/src/vitest-main.js +1594 -0
- package/packages/datadog-instrumentations/src/vitest-util.js +254 -0
- package/packages/datadog-instrumentations/src/vitest-worker.js +823 -0
- package/packages/datadog-instrumentations/src/vitest.js +11 -1855
- package/packages/datadog-plugin-ai/src/index.js +1 -1
- package/packages/datadog-plugin-ai/src/tracing.js +66 -3
- package/packages/datadog-plugin-ai/src/utils.js +17 -4
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/client.js +2 -2
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/context.js +19 -10
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/handler.js +1 -0
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +47 -11
- package/packages/datadog-plugin-child_process/src/index.js +13 -2
- package/packages/datadog-plugin-claude-agent-sdk/src/index.js +31 -0
- package/packages/datadog-plugin-claude-agent-sdk/src/tracing.js +107 -0
- package/packages/datadog-plugin-claude-agent-sdk/src/util.js +15 -0
- package/packages/datadog-plugin-cucumber/src/index.js +15 -24
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +360 -14
- package/packages/datadog-plugin-cypress/src/index.js +47 -2
- package/packages/datadog-plugin-cypress/src/plugin.js +1 -0
- package/packages/datadog-plugin-cypress/src/support.js +45 -1
- package/packages/datadog-plugin-express/src/code_origin.js +1 -1
- package/packages/datadog-plugin-fastify/src/code_origin.js +1 -1
- package/packages/datadog-plugin-graphql/src/execute.js +639 -12
- package/packages/datadog-plugin-graphql/src/index.js +37 -9
- package/packages/datadog-plugin-graphql/src/parse.js +24 -4
- package/packages/datadog-plugin-graphql/src/utils.js +9 -2
- package/packages/datadog-plugin-graphql/src/validate.js +17 -6
- package/packages/datadog-plugin-http/src/index.js +6 -8
- package/packages/datadog-plugin-jest/src/index.js +31 -15
- package/packages/datadog-plugin-mocha/src/index.js +40 -15
- package/packages/datadog-plugin-mongodb-core/src/bulk-write.js +43 -0
- package/packages/datadog-plugin-mongodb-core/src/index.js +8 -443
- package/packages/datadog-plugin-mongodb-core/src/query.js +452 -0
- package/packages/datadog-plugin-openai/src/services.js +2 -2
- package/packages/datadog-plugin-playwright/src/index.js +4 -3
- package/packages/datadog-plugin-vitest/src/index.js +120 -72
- package/packages/datadog-shimmer/src/shimmer.js +37 -16
- package/packages/dd-trace/src/aiguard/index.js +9 -14
- package/packages/dd-trace/src/aiguard/integrations/index.js +34 -0
- package/packages/dd-trace/src/aiguard/integrations/openai.js +47 -33
- package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +42 -28
- package/packages/dd-trace/src/aiguard/sdk.js +2 -2
- package/packages/dd-trace/src/appsec/api_security/sampler.js +3 -3
- package/packages/dd-trace/src/appsec/channels.js +3 -1
- package/packages/dd-trace/src/appsec/downstream_requests.js +4 -4
- package/packages/dd-trace/src/appsec/graphql.js +14 -11
- package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +23 -23
- package/packages/dd-trace/src/appsec/iast/security-controls/index.js +2 -2
- package/packages/dd-trace/src/appsec/iast/telemetry/index.js +2 -1
- package/packages/dd-trace/src/appsec/index.js +10 -1
- package/packages/dd-trace/src/appsec/lambda.js +135 -0
- package/packages/dd-trace/src/appsec/reporter.js +49 -10
- package/packages/dd-trace/src/appsec/telemetry/index.js +1 -1
- package/packages/dd-trace/src/appsec/waf/index.js +16 -4
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +4 -4
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +26 -1
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +115 -24
- package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +21 -6
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +5 -2
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +6 -2
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +130 -20
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +2 -2
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +17 -0
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +47 -26
- package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +2 -2
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +64 -53
- package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +3 -3
- package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +156 -0
- package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +23 -5
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +128 -0
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +287 -0
- package/packages/dd-trace/src/config/defaults.js +3 -2
- package/packages/dd-trace/src/config/generated-config-types.d.ts +71 -57
- package/packages/dd-trace/src/config/helper.js +1 -0
- package/packages/dd-trace/src/config/index.js +35 -22
- package/packages/dd-trace/src/config/major-overrides.js +4 -2
- package/packages/dd-trace/src/config/remote_config.js +1 -1
- package/packages/dd-trace/src/config/supported-configurations.json +127 -56
- package/packages/dd-trace/src/constants.js +7 -0
- package/packages/dd-trace/src/crashtracking/crashtracker.js +2 -2
- package/packages/dd-trace/src/datastreams/processor.js +11 -3
- package/packages/dd-trace/src/exporters/agent/index.js +1 -1
- package/packages/dd-trace/src/exporters/agentless/writer.js +4 -4
- package/packages/dd-trace/src/llmobs/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +403 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/index.js +8 -395
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +27 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/vercelTelemetry.js +363 -0
- package/packages/dd-trace/src/llmobs/plugins/base.js +4 -4
- package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/index.js +270 -0
- package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/utils.js +10 -0
- package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
- package/packages/dd-trace/src/llmobs/sdk.js +3 -3
- package/packages/dd-trace/src/llmobs/span_processor.js +1 -1
- package/packages/dd-trace/src/llmobs/tagger.js +3 -3
- package/packages/dd-trace/src/llmobs/writers/base.js +1 -1
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +54 -5
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +40 -0
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +169 -0
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +38 -39
- package/packages/dd-trace/src/opentelemetry/span.js +5 -0
- package/packages/dd-trace/src/opentelemetry/trace/index.js +4 -0
- package/packages/dd-trace/src/opentelemetry/tracer_provider.js +1 -1
- package/packages/dd-trace/src/opentracing/tracer.js +6 -1
- package/packages/dd-trace/src/plugin_manager.js +23 -7
- package/packages/dd-trace/src/plugins/ci_plugin.js +159 -10
- package/packages/dd-trace/src/plugins/index.js +2 -0
- package/packages/dd-trace/src/plugins/util/llm.js +6 -1
- package/packages/dd-trace/src/plugins/util/test.js +15 -9
- package/packages/dd-trace/src/profiling/exporter_cli.js +2 -2
- package/packages/dd-trace/src/profiling/exporters/agent.js +28 -3
- package/packages/dd-trace/src/profiling/exporters/event_serializer.js +3 -3
- package/packages/dd-trace/src/profiling/profilers/wall.js +1 -1
- package/packages/dd-trace/src/proxy.js +8 -8
- package/packages/dd-trace/src/span_processor.js +5 -6
- package/packages/dd-trace/src/span_stats.js +96 -78
- package/packages/dd-trace/src/startup-log.js +2 -1
- package/packages/dd-trace/src/telemetry/dependencies.js +1 -1
- package/packages/dd-trace/src/telemetry/endpoints.js +6 -4
- package/packages/dd-trace/src/telemetry/index.js +2 -2
- package/packages/dd-trace/src/telemetry/logs/index.js +1 -1
- package/packages/dd-trace/src/telemetry/send-data.js +8 -8
- package/packages/dd-trace/src/telemetry/session-propagation.js +1 -1
- package/packages/dd-trace/src/telemetry/telemetry.js +9 -9
- package/vendor/dist/@datadog/sketches-js/index.js +1 -1
- package/vendor/dist/protobufjs/index.js +1 -1
- package/vendor/dist/protobufjs/minimal/index.js +1 -1
- package/vendor/dist/shell-quote/index.js +1 -1
- package/packages/datadog-plugin-graphql/src/resolve.js +0 -170
|
@@ -36,6 +36,7 @@ const {
|
|
|
36
36
|
getOnTestEndHandler,
|
|
37
37
|
getOnTestRetryHandler,
|
|
38
38
|
getOnHookEndHandler,
|
|
39
|
+
patchFailedTestReplayHookUp,
|
|
39
40
|
getOnFailHandler,
|
|
40
41
|
getOnPendingHandler,
|
|
41
42
|
testFileToSuiteCtx,
|
|
@@ -382,6 +383,10 @@ function applyTestManagementTestsResponse ({ err, testManagementTests: receivedT
|
|
|
382
383
|
}
|
|
383
384
|
}
|
|
384
385
|
|
|
386
|
+
function isFailedTestReplayEnabled () {
|
|
387
|
+
return config.isTestDynamicInstrumentationEnabled && config.isDiEnabled
|
|
388
|
+
}
|
|
389
|
+
|
|
385
390
|
function getExecutionConfiguration (runner, isParallel, frameworkVersion, onFinishRequest, localSuites) {
|
|
386
391
|
const ctx = {
|
|
387
392
|
isParallel,
|
|
@@ -471,7 +476,7 @@ function getExecutionConfiguration (runner, isParallel, frameworkVersion, onFini
|
|
|
471
476
|
}
|
|
472
477
|
}
|
|
473
478
|
|
|
474
|
-
const onReceivedConfiguration = ({ err, libraryConfig, repositoryRoot }) => {
|
|
479
|
+
const onReceivedConfiguration = ({ err, isTestDynamicInstrumentationEnabled, libraryConfig, repositoryRoot }) => {
|
|
475
480
|
if (err || !skippableSuitesCh.hasSubscribers || !knownTestsCh.hasSubscribers) {
|
|
476
481
|
return mochaGlobalRunCh.runStores(ctx, () => {
|
|
477
482
|
onFinishRequest()
|
|
@@ -492,6 +497,8 @@ function getExecutionConfiguration (runner, isParallel, frameworkVersion, onFini
|
|
|
492
497
|
config.isSuitesSkippingEnabled = config.isItrEnabled && libraryConfig.isSuitesSkippingEnabled
|
|
493
498
|
config.isFlakyTestRetriesEnabled = libraryConfig.isFlakyTestRetriesEnabled
|
|
494
499
|
config.flakyTestRetriesCount = libraryConfig.flakyTestRetriesCount
|
|
500
|
+
config.isDiEnabled = libraryConfig.isDiEnabled
|
|
501
|
+
config.isTestDynamicInstrumentationEnabled = isTestDynamicInstrumentationEnabled
|
|
495
502
|
|
|
496
503
|
getTestOptimizationRequestResults({
|
|
497
504
|
isKnownTestsEnabled: config.isKnownTestsEnabled,
|
|
@@ -555,6 +562,9 @@ addHook({
|
|
|
555
562
|
})
|
|
556
563
|
|
|
557
564
|
getExecutionConfiguration(runner, false, frameworkVersion, () => {
|
|
565
|
+
if (isFailedTestReplayEnabled()) {
|
|
566
|
+
patchFailedTestReplayHookUp(runner.constructor)
|
|
567
|
+
}
|
|
558
568
|
if (config.isKnownTestsEnabled) {
|
|
559
569
|
const testSuites = this.files.map(file => getTestSuitePath(file, process.cwd()))
|
|
560
570
|
const isFaulty = getIsFaultyEarlyFlakeDetection(
|
|
@@ -780,7 +790,13 @@ addHook({
|
|
|
780
790
|
this.on('retry', getOnTestRetryHandler(config))
|
|
781
791
|
|
|
782
792
|
// If the hook passes, 'hook end' will be emitted. Otherwise, 'fail' will be emitted
|
|
783
|
-
this.on('hook end', getOnHookEndHandler(config
|
|
793
|
+
this.on('hook end', getOnHookEndHandler(config, {
|
|
794
|
+
onStart: incrementPendingRootFinalization,
|
|
795
|
+
onFinish: function (test) {
|
|
796
|
+
finishRootSuiteAfterFinalAttempt(test)
|
|
797
|
+
decrementPendingRootFinalization(test)
|
|
798
|
+
},
|
|
799
|
+
}))
|
|
784
800
|
|
|
785
801
|
this.on('hook end', function (hook) {
|
|
786
802
|
const test = hook.ctx?.currentTest
|
|
@@ -1095,7 +1111,8 @@ addHook({
|
|
|
1095
1111
|
(!config.isKnownTestsEnabled &&
|
|
1096
1112
|
!config.isTestManagementTestsEnabled &&
|
|
1097
1113
|
!config.isImpactedTestsEnabled &&
|
|
1098
|
-
!config.isFlakyTestRetriesEnabled
|
|
1114
|
+
!config.isFlakyTestRetriesEnabled &&
|
|
1115
|
+
!isFailedTestReplayEnabled())) {
|
|
1099
1116
|
return run.apply(this, arguments)
|
|
1100
1117
|
}
|
|
1101
1118
|
|
|
@@ -1146,6 +1163,10 @@ addHook({
|
|
|
1146
1163
|
newWorkerArgs._ddFlakyTestRetriesCount = config.flakyTestRetriesCount
|
|
1147
1164
|
}
|
|
1148
1165
|
|
|
1166
|
+
if (isFailedTestReplayEnabled()) {
|
|
1167
|
+
newWorkerArgs._ddIsFailedTestReplayEnabled = true
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1149
1170
|
// We pass the known tests for the test file to the worker
|
|
1150
1171
|
const testFileResult = await run.apply(
|
|
1151
1172
|
this,
|
|
@@ -2,9 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
const { performance } = require('node:perf_hooks')
|
|
4
4
|
|
|
5
|
-
// Capture real timers at module load time, before any test can install fake timers.
|
|
6
|
-
const realSetTimeout = setTimeout
|
|
7
|
-
|
|
8
5
|
const {
|
|
9
6
|
getTestSuitePath,
|
|
10
7
|
DYNAMIC_NAME_RE,
|
|
@@ -19,6 +16,7 @@ const shimmer = require('../../../datadog-shimmer')
|
|
|
19
16
|
// test channels
|
|
20
17
|
const testStartCh = channel('ci:mocha:test:start')
|
|
21
18
|
const testFinishCh = channel('ci:mocha:test:finish')
|
|
19
|
+
const testDiWaitCh = channel('ci:mocha:test:di:wait')
|
|
22
20
|
// after a test has failed, we'll publish to this channel
|
|
23
21
|
const testRetryCh = channel('ci:mocha:test:retry')
|
|
24
22
|
const errorCh = channel('ci:mocha:test:error')
|
|
@@ -28,7 +26,6 @@ const isModifiedCh = channel('ci:mocha:test:is-modified')
|
|
|
28
26
|
// suite channels
|
|
29
27
|
const testSuiteErrorCh = channel('ci:mocha:test-suite:error')
|
|
30
28
|
|
|
31
|
-
const BREAKPOINT_HIT_GRACE_PERIOD_MS = 200
|
|
32
29
|
const testToContext = new WeakMap()
|
|
33
30
|
const originalFns = new WeakMap()
|
|
34
31
|
const testToStartLine = new WeakMap()
|
|
@@ -43,6 +40,12 @@ const testsStatuses = new Map()
|
|
|
43
40
|
const efdRetryCountByTestFullName = new Map()
|
|
44
41
|
const efdSlowAbortedTests = new Set()
|
|
45
42
|
const attemptToFixExecutions = new Map()
|
|
43
|
+
|
|
44
|
+
function waitForHitProbe () {
|
|
45
|
+
const promises = {}
|
|
46
|
+
testDiWaitCh.publish({ promises })
|
|
47
|
+
return promises.hitBreakpointPromise
|
|
48
|
+
}
|
|
46
49
|
const loggedAttemptToFixTests = new Set()
|
|
47
50
|
|
|
48
51
|
function getAfterEachHooks (testOrHook) {
|
|
@@ -639,14 +642,8 @@ function getOnTestEndHandler (config, finalAttemptHandlers) {
|
|
|
639
642
|
finalAttemptHandlers?.onStart?.(test)
|
|
640
643
|
}
|
|
641
644
|
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
if (test._ddShouldWaitForHitProbe || test._retriedTest?._ddShouldWaitForHitProbe) {
|
|
645
|
-
await new Promise((resolve) => {
|
|
646
|
-
realSetTimeout(() => {
|
|
647
|
-
resolve()
|
|
648
|
-
}, BREAKPOINT_HIT_GRACE_PERIOD_MS)
|
|
649
|
-
})
|
|
645
|
+
if (test._retriedTest?._ddShouldWaitForHitProbe) {
|
|
646
|
+
await waitForHitProbe()
|
|
650
647
|
}
|
|
651
648
|
|
|
652
649
|
if (shouldFinishTest) {
|
|
@@ -665,7 +662,7 @@ function getOnTestEndHandler (config, finalAttemptHandlers) {
|
|
|
665
662
|
}
|
|
666
663
|
}
|
|
667
664
|
|
|
668
|
-
function getOnHookEndHandler (config) {
|
|
665
|
+
function getOnHookEndHandler (config, finalAttemptHandlers) {
|
|
669
666
|
return function (hook) {
|
|
670
667
|
const test = hook.ctx.currentTest
|
|
671
668
|
const afterEachHooks = getAfterEachHooks(hook)
|
|
@@ -678,22 +675,118 @@ function getOnHookEndHandler (config) {
|
|
|
678
675
|
// skip to avoid double-publishing
|
|
679
676
|
if (ctx && (!test._ddIsDisabled || test._ddIsAttemptToFix)) {
|
|
680
677
|
const testFinishInfo = getTestFinishInfo(test, status, config, ctx.err || test.err)
|
|
681
|
-
|
|
682
|
-
|
|
678
|
+
const isFinalAttempt = testFinishInfo.finalStatus !== undefined
|
|
679
|
+
const publishTestFinish = () => {
|
|
680
|
+
testFinishCh.publish({
|
|
681
|
+
status,
|
|
682
|
+
hasBeenRetried: isMochaRetry(test),
|
|
683
|
+
isLastRetry: getIsLastRetry(test),
|
|
684
|
+
...testFinishInfo,
|
|
685
|
+
...ctx.currentStore,
|
|
686
|
+
})
|
|
687
|
+
if (isFinalAttempt) {
|
|
688
|
+
test._ddIsFinalAttempt = true
|
|
689
|
+
}
|
|
683
690
|
}
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
+
if (test._retriedTest?._ddShouldWaitForHitProbe) {
|
|
692
|
+
if (isFinalAttempt) {
|
|
693
|
+
finalAttemptHandlers?.onStart?.(test)
|
|
694
|
+
}
|
|
695
|
+
test._ddDeferredHookEnd = {
|
|
696
|
+
waitForHitProbePromise: waitForHitProbe(),
|
|
697
|
+
publishTestFinish,
|
|
698
|
+
onFinish: isFinalAttempt ? () => finalAttemptHandlers?.onFinish?.(test) : undefined,
|
|
699
|
+
}
|
|
700
|
+
return
|
|
701
|
+
}
|
|
702
|
+
publishTestFinish()
|
|
691
703
|
}
|
|
692
704
|
}
|
|
693
705
|
}
|
|
694
706
|
}
|
|
695
707
|
}
|
|
696
708
|
|
|
709
|
+
function finishDeferredHookEnd (test) {
|
|
710
|
+
const deferredHookEnd = test?._ddDeferredHookEnd
|
|
711
|
+
if (!deferredHookEnd) return
|
|
712
|
+
|
|
713
|
+
const finish = () => {
|
|
714
|
+
try {
|
|
715
|
+
return deferredHookEnd.publishTestFinish()
|
|
716
|
+
} finally {
|
|
717
|
+
deferredHookEnd.onFinish?.()
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
delete test._ddDeferredHookEnd
|
|
722
|
+
if (!deferredHookEnd.waitForHitProbePromise) return finish()
|
|
723
|
+
|
|
724
|
+
return deferredHookEnd.waitForHitProbePromise.then(
|
|
725
|
+
finish,
|
|
726
|
+
finish
|
|
727
|
+
)
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
/**
|
|
731
|
+
* Runs a Failed Test Replay hookUp callback after pending DI operations that must happen first.
|
|
732
|
+
*
|
|
733
|
+
* @param {(...args: unknown[]) => unknown} fn - Original hookUp completion callback.
|
|
734
|
+
* @param {object} test - Mocha test currently owning the hook.
|
|
735
|
+
* @param {Promise<void>|undefined} failedTestReplayPromise - Pending Failed Test Replay wait, if any.
|
|
736
|
+
* @param {unknown} hookThis - Callback receiver.
|
|
737
|
+
* @param {IArguments} args - Arguments passed by Mocha.
|
|
738
|
+
* @returns {unknown}
|
|
739
|
+
*/
|
|
740
|
+
function runFailedTestReplayHookUpCallback (fn, test, failedTestReplayPromise, hookThis, args) {
|
|
741
|
+
const continueAfterProbe = () => {
|
|
742
|
+
const deferredHookEndPromise = finishDeferredHookEnd(test)
|
|
743
|
+
if (deferredHookEndPromise) {
|
|
744
|
+
return deferredHookEndPromise.then(() => fn.apply(hookThis, args), () => fn.apply(hookThis, args))
|
|
745
|
+
}
|
|
746
|
+
return fn.apply(hookThis, args)
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
if (failedTestReplayPromise) {
|
|
750
|
+
return failedTestReplayPromise.then(continueAfterProbe, continueAfterProbe)
|
|
751
|
+
}
|
|
752
|
+
return continueAfterProbe()
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
/**
|
|
756
|
+
* Wraps Mocha's hookUp completion callback so retries wait for DI before continuing.
|
|
757
|
+
*
|
|
758
|
+
* @param {(...args: unknown[]) => unknown} fn - Original hookUp completion callback.
|
|
759
|
+
* @param {object} test - Mocha test currently owning the hook.
|
|
760
|
+
* @param {Promise<void>|undefined} failedTestReplayPromise - Pending Failed Test Replay wait, if any.
|
|
761
|
+
* @returns {(...args: unknown[]) => unknown}
|
|
762
|
+
*/
|
|
763
|
+
function wrapFailedTestReplayHookUpCallback (fn, test, failedTestReplayPromise) {
|
|
764
|
+
return shimmer.wrapCallback(fn, fn => function () {
|
|
765
|
+
return runFailedTestReplayHookUpCallback(fn, test, failedTestReplayPromise, this, arguments)
|
|
766
|
+
})
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
const patchedFailedTestReplayHookUp = new WeakSet()
|
|
770
|
+
|
|
771
|
+
function patchFailedTestReplayHookUp (Runner) {
|
|
772
|
+
if (patchedFailedTestReplayHookUp.has(Runner)) return
|
|
773
|
+
|
|
774
|
+
patchedFailedTestReplayHookUp.add(Runner)
|
|
775
|
+
shimmer.wrap(Runner.prototype, 'hookUp', hookUp => function (name, fn) {
|
|
776
|
+
const test = name === 'afterEach' && this.test
|
|
777
|
+
if (!test) {
|
|
778
|
+
return hookUp.apply(this, arguments)
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
const failedTestReplayPromise = test._ddFailedTestReplayPromise
|
|
782
|
+
if (failedTestReplayPromise) {
|
|
783
|
+
delete test._ddFailedTestReplayPromise
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
return hookUp.call(this, name, wrapFailedTestReplayHookUpCallback(fn, test, failedTestReplayPromise))
|
|
787
|
+
})
|
|
788
|
+
}
|
|
789
|
+
|
|
697
790
|
function getOnFailHandler (isMain, config) {
|
|
698
791
|
return function (testOrHook, err) {
|
|
699
792
|
const testFile = testOrHook.file
|
|
@@ -764,14 +857,24 @@ function getOnTestRetryHandler (config) {
|
|
|
764
857
|
config.isFlakyTestRetriesEnabled &&
|
|
765
858
|
!test._ddIsAttemptToFix &&
|
|
766
859
|
!test._ddIsEfdRetry
|
|
860
|
+
const promises = {}
|
|
767
861
|
testRetryCh.publish({
|
|
768
862
|
isFirstAttempt,
|
|
769
863
|
err,
|
|
770
864
|
willBeRetried,
|
|
771
865
|
test,
|
|
772
866
|
isAtrRetry,
|
|
867
|
+
promises,
|
|
773
868
|
...ctx.currentStore,
|
|
774
869
|
})
|
|
870
|
+
if (promises.setProbePromise && promises.finishTestPromise) {
|
|
871
|
+
test._ddFailedTestReplayPromise = Promise.all([
|
|
872
|
+
promises.setProbePromise,
|
|
873
|
+
promises.finishTestPromise,
|
|
874
|
+
]).then(() => {})
|
|
875
|
+
} else if (promises.setProbePromise || promises.finishTestPromise) {
|
|
876
|
+
test._ddFailedTestReplayPromise = promises.setProbePromise || promises.finishTestPromise
|
|
877
|
+
}
|
|
775
878
|
}
|
|
776
879
|
const key = getTestToContextKey(test)
|
|
777
880
|
testToContext.delete(key)
|
|
@@ -901,6 +1004,9 @@ module.exports = {
|
|
|
901
1004
|
getOnTestEndHandler,
|
|
902
1005
|
getOnTestRetryHandler,
|
|
903
1006
|
getOnHookEndHandler,
|
|
1007
|
+
finishDeferredHookEnd,
|
|
1008
|
+
wrapFailedTestReplayHookUpCallback,
|
|
1009
|
+
patchFailedTestReplayHookUp,
|
|
904
1010
|
getOnFailHandler,
|
|
905
1011
|
getOnPendingHandler,
|
|
906
1012
|
testFileToSuiteCtx,
|
|
@@ -13,6 +13,7 @@ const {
|
|
|
13
13
|
getOnPendingHandler,
|
|
14
14
|
getOnTestRetryHandler,
|
|
15
15
|
getRunTestsWrapper,
|
|
16
|
+
patchFailedTestReplayHookUp,
|
|
16
17
|
} = require('./utils')
|
|
17
18
|
require('./common')
|
|
18
19
|
|
|
@@ -22,6 +23,10 @@ const workerFinishCh = channel('ci:mocha:worker:finish')
|
|
|
22
23
|
|
|
23
24
|
const config = {}
|
|
24
25
|
|
|
26
|
+
function isFailedTestReplayEnabled () {
|
|
27
|
+
return config.isTestDynamicInstrumentationEnabled && config.isDiEnabled
|
|
28
|
+
}
|
|
29
|
+
|
|
25
30
|
addHook({
|
|
26
31
|
name: 'mocha',
|
|
27
32
|
versions: ['>=8.0.0'],
|
|
@@ -60,6 +65,11 @@ addHook({
|
|
|
60
65
|
delete this.options._ddIsFlakyTestRetriesEnabled
|
|
61
66
|
delete this.options._ddFlakyTestRetriesCount
|
|
62
67
|
}
|
|
68
|
+
if (this.options._ddIsFailedTestReplayEnabled) {
|
|
69
|
+
config.isTestDynamicInstrumentationEnabled = true
|
|
70
|
+
config.isDiEnabled = true
|
|
71
|
+
delete this.options._ddIsFailedTestReplayEnabled
|
|
72
|
+
}
|
|
63
73
|
return run.apply(this, args)
|
|
64
74
|
})
|
|
65
75
|
|
|
@@ -78,6 +88,9 @@ addHook({
|
|
|
78
88
|
if (!workerFinishCh.hasSubscribers) {
|
|
79
89
|
return run.apply(this, args)
|
|
80
90
|
}
|
|
91
|
+
if (isFailedTestReplayEnabled()) {
|
|
92
|
+
patchFailedTestReplayHookUp(Runner)
|
|
93
|
+
}
|
|
81
94
|
// We flush when the worker ends with its test file (a mocha instance in a worker runs a single test file)
|
|
82
95
|
this.once('end', () => {
|
|
83
96
|
workerFinishCh.publish()
|
|
@@ -28,6 +28,10 @@ const collectionMethodsWithTwoFilters = [
|
|
|
28
28
|
|
|
29
29
|
const startCh = channel('datadog:mongodb:collection:filter:start')
|
|
30
30
|
|
|
31
|
+
const bulkWriteStartCh = channel('apm:mongodb:bulkwrite:start')
|
|
32
|
+
const bulkWriteFinishCh = channel('apm:mongodb:bulkwrite:finish')
|
|
33
|
+
const bulkWriteErrorCh = channel('apm:mongodb:bulkwrite:error')
|
|
34
|
+
|
|
31
35
|
addHook({ name: 'mongodb', versions: ['>=3.3 <5', '5', '>=6'] }, mongodb => {
|
|
32
36
|
for (const methodName of [...collectionMethodsWithFilter, ...collectionMethodsWithTwoFilters]) {
|
|
33
37
|
if (!(methodName in mongodb.Collection.prototype)) continue
|
|
@@ -55,5 +59,75 @@ addHook({ name: 'mongodb', versions: ['>=3.3 <5', '5', '>=6'] }, mongodb => {
|
|
|
55
59
|
}
|
|
56
60
|
})
|
|
57
61
|
}
|
|
62
|
+
|
|
63
|
+
// `bulkWrite` fans out into separate `insert`/`update`/`delete` wire commands, so wrap
|
|
64
|
+
// it to open one parent span those per-type commands nest under as children.
|
|
65
|
+
if ('bulkWrite' in mongodb.Collection.prototype) {
|
|
66
|
+
shimmer.wrap(mongodb.Collection.prototype, 'bulkWrite', wrapBulkWrite)
|
|
67
|
+
}
|
|
68
|
+
|
|
58
69
|
return mongodb
|
|
59
70
|
})
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @param {Function} bulkWrite
|
|
74
|
+
* @returns {Function}
|
|
75
|
+
*/
|
|
76
|
+
function wrapBulkWrite (bulkWrite) {
|
|
77
|
+
return function (...args) {
|
|
78
|
+
/* istanbul ignore if: plugin stays subscribed for the whole suite; the disabled fast path is unreachable */
|
|
79
|
+
if (!bulkWriteStartCh.hasSubscribers) {
|
|
80
|
+
return bulkWrite.apply(this, args)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const ctx = { ns: this.namespace }
|
|
84
|
+
|
|
85
|
+
return bulkWriteStartCh.runStores(ctx, () => {
|
|
86
|
+
// Pre-v5 drivers take a trailing callback and return `undefined`; v5+ ignore any
|
|
87
|
+
// extra argument and always return a promise. Wrap a trailing callback for the
|
|
88
|
+
// legacy path and finish on the returned promise when there is one, so both APIs are
|
|
89
|
+
// covered without sniffing the version. Pre-v5 also validates arguments synchronously,
|
|
90
|
+
// so guard the call to finish the span instead of leaking it on a throw.
|
|
91
|
+
const lastIndex = args.length - 1
|
|
92
|
+
const callback = args[lastIndex]
|
|
93
|
+
if (typeof callback === 'function') {
|
|
94
|
+
args[lastIndex] = shimmer.wrapCallback(callback, callback => function (error) {
|
|
95
|
+
if (error) {
|
|
96
|
+
ctx.error = error
|
|
97
|
+
bulkWriteErrorCh.publish(ctx)
|
|
98
|
+
}
|
|
99
|
+
return bulkWriteFinishCh.runStores(ctx, callback, this, ...arguments)
|
|
100
|
+
})
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
let result
|
|
104
|
+
try {
|
|
105
|
+
result = bulkWrite.apply(this, args)
|
|
106
|
+
} catch (error) {
|
|
107
|
+
finishBulkWriteError(ctx, error)
|
|
108
|
+
throw error
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (result !== undefined && typeof result.then === 'function') {
|
|
112
|
+
result.then(function (value) {
|
|
113
|
+
ctx.result = value
|
|
114
|
+
bulkWriteFinishCh.publish(ctx)
|
|
115
|
+
}, function (error) {
|
|
116
|
+
finishBulkWriteError(ctx, error)
|
|
117
|
+
})
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return result
|
|
121
|
+
})
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* @param {{ error?: unknown }} ctx
|
|
127
|
+
* @param {unknown} error
|
|
128
|
+
*/
|
|
129
|
+
function finishBulkWriteError (ctx, error) {
|
|
130
|
+
ctx.error = error
|
|
131
|
+
bulkWriteErrorCh.publish(ctx)
|
|
132
|
+
bulkWriteFinishCh.publish(ctx)
|
|
133
|
+
}
|
|
@@ -6,6 +6,11 @@ const { wrapThen } = require('./helpers/promise')
|
|
|
6
6
|
|
|
7
7
|
const startCh = channel('datadog:mongoose:model:filter:start')
|
|
8
8
|
const finishCh = channel('datadog:mongoose:model:filter:finish')
|
|
9
|
+
// Bound around the deferred query execution. The `bindStore` transform returns a
|
|
10
|
+
// child store with the analysis marker set, covering the whole async scope that
|
|
11
|
+
// reaches the mongodb driver. `runStores` enters the child only for that scope
|
|
12
|
+
// and restores the parent on its own.
|
|
13
|
+
const execCh = channel('datadog:mongoose:model:filter:exec')
|
|
9
14
|
// this channel is for wrapping the callback of exec methods and handling store context
|
|
10
15
|
const execStartCh = channel('apm:mongoose:exec:start')
|
|
11
16
|
const execFinishCh = channel('apm:mongoose:exec:finish')
|
|
@@ -103,7 +108,13 @@ addHook({
|
|
|
103
108
|
return startCh.runStores(ctx, () => {
|
|
104
109
|
wrapCallbackIfExist(args, ctx)
|
|
105
110
|
|
|
106
|
-
|
|
111
|
+
// A callback query executes synchronously inside `method.apply`, so the
|
|
112
|
+
// exec scope (and the deferred driver call it spawns) must be entered
|
|
113
|
+
// here. A promise query defers execution to a later `exec`/`then`, which
|
|
114
|
+
// is bound separately below.
|
|
115
|
+
const res = callbackWrapped
|
|
116
|
+
? execCh.runStores(ctx, () => method.apply(this, args))
|
|
117
|
+
: method.apply(this, args)
|
|
107
118
|
|
|
108
119
|
// if it is not callback, wrap exec method and its then
|
|
109
120
|
if (!callbackWrapped) {
|
|
@@ -113,7 +124,7 @@ addHook({
|
|
|
113
124
|
wrapCallbackIfExist(args, ctx)
|
|
114
125
|
}
|
|
115
126
|
|
|
116
|
-
const execResult = originalExec.apply(this, args)
|
|
127
|
+
const execResult = execCh.runStores(ctx, () => originalExec.apply(this, args))
|
|
117
128
|
|
|
118
129
|
if (callbackWrapped || typeof execResult?.then !== 'function') {
|
|
119
130
|
return execResult
|
|
@@ -840,6 +840,8 @@ function testEndHandler ({
|
|
|
840
840
|
test._ddHasFailedAllRetries = true
|
|
841
841
|
}
|
|
842
842
|
|
|
843
|
+
const willRetry = testWillRetry(test, testStatus)
|
|
844
|
+
|
|
843
845
|
// this handles tests that do not go through the worker process (because they're skipped)
|
|
844
846
|
if (shouldCreateTestSpan) {
|
|
845
847
|
const testResult = results.at(-1)
|
|
@@ -850,7 +852,7 @@ function testEndHandler ({
|
|
|
850
852
|
!test._ddIsEfdRetry
|
|
851
853
|
|
|
852
854
|
const finalStatus = getFinalStatus({
|
|
853
|
-
isFinalExecution: !
|
|
855
|
+
isFinalExecution: !willRetry,
|
|
854
856
|
isDisabled: test._ddIsDisabled,
|
|
855
857
|
isQuarantined: test._ddIsQuarantined,
|
|
856
858
|
isAtrRetry,
|
|
@@ -888,17 +890,19 @@ function testEndHandler ({
|
|
|
888
890
|
}
|
|
889
891
|
}
|
|
890
892
|
|
|
891
|
-
if (
|
|
892
|
-
testSuiteToTestStatuses.
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
893
|
+
if (!willRetry) {
|
|
894
|
+
if (testSuiteToTestStatuses.has(testSuiteAbsolutePath)) {
|
|
895
|
+
testSuiteToTestStatuses.get(testSuiteAbsolutePath).push(testStatus)
|
|
896
|
+
} else {
|
|
897
|
+
testSuiteToTestStatuses.set(testSuiteAbsolutePath, [testStatus])
|
|
898
|
+
}
|
|
896
899
|
|
|
897
|
-
|
|
898
|
-
|
|
900
|
+
if (error) {
|
|
901
|
+
addErrorToTestSuite(testSuiteAbsolutePath, error)
|
|
902
|
+
}
|
|
899
903
|
}
|
|
900
904
|
|
|
901
|
-
if (!
|
|
905
|
+
if (!willRetry) {
|
|
902
906
|
remainingTestsByFile[testSuiteAbsolutePath] = remainingTestsByFile[testSuiteAbsolutePath]
|
|
903
907
|
.filter(currentTest => currentTest !== test)
|
|
904
908
|
}
|