dd-trace 5.102.0 → 5.104.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/ext/exporters.js +1 -0
- package/index.d.ts +25 -3
- package/package.json +15 -13
- package/packages/datadog-esbuild/src/utils.js +2 -2
- package/packages/datadog-instrumentations/src/ai.js +1 -1
- package/packages/datadog-instrumentations/src/aws-sdk.js +2 -2
- package/packages/datadog-instrumentations/src/cassandra-driver.js +5 -2
- package/packages/datadog-instrumentations/src/confluentinc-kafka-javascript.js +32 -15
- package/packages/datadog-instrumentations/src/couchbase.js +69 -220
- package/packages/datadog-instrumentations/src/cucumber.js +104 -31
- package/packages/datadog-instrumentations/src/elasticsearch.js +4 -4
- package/packages/datadog-instrumentations/src/electron/preload.js +42 -0
- package/packages/datadog-instrumentations/src/electron.js +240 -0
- package/packages/datadog-instrumentations/src/fetch.js +5 -5
- package/packages/datadog-instrumentations/src/graphql.js +13 -17
- package/packages/datadog-instrumentations/src/grpc/client.js +48 -32
- package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +2 -2
- package/packages/datadog-instrumentations/src/helpers/hook.js +4 -1
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/instrument.js +2 -2
- package/packages/datadog-instrumentations/src/helpers/kafka.js +58 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +3 -2
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +19 -5
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +14 -13
- package/packages/datadog-instrumentations/src/http/client.js +2 -2
- package/packages/datadog-instrumentations/src/ioredis.js +18 -14
- package/packages/datadog-instrumentations/src/jest.js +382 -84
- package/packages/datadog-instrumentations/src/kafkajs.js +184 -174
- package/packages/datadog-instrumentations/src/mariadb.js +1 -1
- package/packages/datadog-instrumentations/src/memcached.js +2 -1
- package/packages/datadog-instrumentations/src/mocha/main.js +309 -56
- package/packages/datadog-instrumentations/src/mocha/utils.js +48 -8
- package/packages/datadog-instrumentations/src/mongodb-core.js +34 -9
- package/packages/datadog-instrumentations/src/mongoose.js +10 -12
- package/packages/datadog-instrumentations/src/mysql.js +2 -2
- package/packages/datadog-instrumentations/src/mysql2.js +1 -1
- package/packages/datadog-instrumentations/src/pg.js +25 -11
- package/packages/datadog-instrumentations/src/playwright.js +449 -60
- package/packages/datadog-instrumentations/src/redis.js +19 -10
- package/packages/datadog-instrumentations/src/router.js +4 -2
- package/packages/datadog-instrumentations/src/vitest.js +246 -149
- package/packages/datadog-plugin-apollo/src/gateway/request.js +1 -21
- package/packages/datadog-plugin-aws-sdk/src/base.js +18 -24
- package/packages/datadog-plugin-aws-sdk/src/services/cloudwatchlogs.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/lambda.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/redshift.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/s3.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/sns.js +2 -2
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/stepfunctions.js +1 -1
- package/packages/datadog-plugin-couchbase/src/index.js +58 -52
- package/packages/datadog-plugin-cucumber/src/index.js +1 -0
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +239 -40
- package/packages/datadog-plugin-cypress/src/support.js +13 -1
- package/packages/datadog-plugin-elasticsearch/src/index.js +28 -8
- package/packages/datadog-plugin-electron/src/index.js +17 -0
- package/packages/datadog-plugin-electron/src/ipc.js +143 -0
- package/packages/datadog-plugin-electron/src/net.js +82 -0
- package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +27 -18
- package/packages/datadog-plugin-graphql/src/execute.js +6 -28
- package/packages/datadog-plugin-graphql/src/resolve.js +30 -35
- package/packages/datadog-plugin-graphql/src/tools/signature.js +32 -7
- package/packages/datadog-plugin-graphql/src/tools/transforms.js +118 -100
- package/packages/datadog-plugin-graphql/src/utils.js +33 -1
- package/packages/datadog-plugin-grpc/src/client.js +6 -7
- package/packages/datadog-plugin-grpc/src/util.js +57 -22
- package/packages/datadog-plugin-http/src/client.js +2 -2
- package/packages/datadog-plugin-jest/src/index.js +92 -50
- package/packages/datadog-plugin-kafkajs/src/producer.js +32 -0
- package/packages/datadog-plugin-mocha/src/index.js +1 -0
- package/packages/datadog-plugin-mongodb-core/src/index.js +70 -69
- package/packages/datadog-plugin-mysql/src/index.js +1 -1
- package/packages/datadog-plugin-openai/src/services.js +2 -1
- package/packages/datadog-plugin-pg/src/index.js +3 -3
- package/packages/datadog-plugin-playwright/src/index.js +4 -0
- package/packages/datadog-plugin-redis/src/index.js +54 -24
- package/packages/datadog-plugin-undici/src/index.js +19 -0
- package/packages/datadog-plugin-vitest/src/index.js +19 -7
- package/packages/datadog-shimmer/src/shimmer.js +35 -0
- package/packages/dd-trace/src/aiguard/index.js +3 -1
- package/packages/dd-trace/src/aiguard/sdk.js +36 -30
- package/packages/dd-trace/src/aiguard/tags.js +20 -11
- package/packages/dd-trace/src/appsec/blocking.js +2 -2
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +2 -2
- package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +1 -1
- package/packages/dd-trace/src/appsec/index.js +10 -3
- package/packages/dd-trace/src/appsec/reporter.js +19 -5
- package/packages/dd-trace/src/azure_metadata.js +17 -6
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +4 -4
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +4 -2
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +6 -4
- package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +1 -1
- package/packages/dd-trace/src/ci-visibility/requests/request.js +3 -1
- package/packages/dd-trace/src/ci-visibility/test-api-manual/test-api-manual-plugin.js +5 -3
- package/packages/dd-trace/src/config/defaults.js +3 -14
- package/packages/dd-trace/src/config/generated-config-types.d.ts +4 -1
- package/packages/dd-trace/src/config/helper.js +4 -0
- package/packages/dd-trace/src/config/index.js +2 -2
- package/packages/dd-trace/src/config/major-overrides.js +98 -0
- package/packages/dd-trace/src/config/parsers.js +7 -1
- package/packages/dd-trace/src/config/supported-configurations.json +60 -38
- package/packages/dd-trace/src/crashtracking/crashtracker.js +15 -3
- package/packages/dd-trace/src/datastreams/checkpointer.js +2 -2
- package/packages/dd-trace/src/datastreams/context.js +4 -2
- package/packages/dd-trace/src/datastreams/manager.js +1 -1
- package/packages/dd-trace/src/datastreams/processor.js +2 -2
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/collector.js +2 -2
- package/packages/dd-trace/src/debugger/devtools_client/source-maps.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/state.js +2 -1
- package/packages/dd-trace/src/debugger/index.js +7 -7
- package/packages/dd-trace/src/dogstatsd.js +2 -2
- package/packages/dd-trace/src/encode/0.4.js +45 -54
- package/packages/dd-trace/src/encode/0.5.js +34 -3
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +26 -19
- package/packages/dd-trace/src/encode/agentless-json.js +1 -1
- package/packages/dd-trace/src/exporter.js +2 -0
- package/packages/dd-trace/src/exporters/agent/index.js +2 -1
- package/packages/dd-trace/src/exporters/agentless/index.js +3 -2
- package/packages/dd-trace/src/exporters/agentless/writer.js +2 -2
- package/packages/dd-trace/src/exporters/common/agents.js +3 -1
- package/packages/dd-trace/src/exporters/common/buffering-exporter.js +2 -1
- package/packages/dd-trace/src/exporters/common/request.js +4 -2
- package/packages/dd-trace/src/exporters/electron/index.js +49 -0
- package/packages/dd-trace/src/external-logger/src/index.js +2 -1
- package/packages/dd-trace/src/git_metadata.js +10 -8
- package/packages/dd-trace/src/id.js +17 -4
- package/packages/dd-trace/src/lambda/handler-paths.js +52 -0
- package/packages/dd-trace/src/lambda/handler.js +2 -4
- package/packages/dd-trace/src/lambda/index.js +62 -14
- package/packages/dd-trace/src/lambda/runtime/patch.js +21 -46
- package/packages/dd-trace/src/llmobs/index.js +13 -2
- package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +45 -15
- package/packages/dd-trace/src/llmobs/sdk.js +10 -0
- package/packages/dd-trace/src/llmobs/writers/base.js +2 -1
- package/packages/dd-trace/src/log/writer.js +3 -1
- package/packages/dd-trace/src/noop/span.js +3 -1
- package/packages/dd-trace/src/openfeature/writers/base.js +2 -1
- package/packages/dd-trace/src/openfeature/writers/exposures.js +51 -20
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +3 -2
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +20 -9
- package/packages/dd-trace/src/payload-tagging/config/index.js +2 -2
- package/packages/dd-trace/src/plugins/apollo.js +3 -1
- package/packages/dd-trace/src/plugins/ci_plugin.js +52 -17
- package/packages/dd-trace/src/plugins/database.js +54 -12
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/log_plugin.js +3 -1
- package/packages/dd-trace/src/plugins/plugin.js +2 -4
- package/packages/dd-trace/src/plugins/tracing.js +5 -3
- package/packages/dd-trace/src/plugins/util/ci.js +8 -8
- package/packages/dd-trace/src/plugins/util/git-cache.js +20 -18
- package/packages/dd-trace/src/plugins/util/git.js +3 -1
- package/packages/dd-trace/src/plugins/util/stacktrace.js +2 -2
- package/packages/dd-trace/src/plugins/util/test.js +119 -5
- package/packages/dd-trace/src/plugins/util/user-provided-git.js +17 -15
- package/packages/dd-trace/src/plugins/util/web.js +11 -0
- package/packages/dd-trace/src/priority_sampler.js +1 -1
- package/packages/dd-trace/src/profiling/profiler.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/wall.js +1 -1
- package/packages/dd-trace/src/profiling/ssi-heuristics.js +1 -1
- package/packages/dd-trace/src/rate_limiter.js +1 -1
- package/packages/dd-trace/src/remote_config/scheduler.js +1 -1
- package/packages/dd-trace/src/ritm.js +2 -1
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +5 -8
- package/packages/dd-trace/src/scope.js +7 -5
- package/packages/dd-trace/src/serverless.js +5 -2
- package/packages/dd-trace/src/service-naming/extra-services.js +14 -0
- package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +20 -0
- package/packages/dd-trace/src/service-naming/schemas/v0/web.js +4 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +20 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/web.js +4 -0
- package/packages/dd-trace/src/span_stats.js +1 -1
- package/packages/dd-trace/src/telemetry/dependencies.js +1 -1
- package/packages/dd-trace/src/telemetry/endpoints.js +1 -1
- package/packages/dd-trace/src/telemetry/telemetry.js +2 -2
- package/packages/dd-trace/src/lambda/runtime/ritm.js +0 -133
- package/vendor/dist/opentracing/LICENSE +0 -201
- package/vendor/dist/opentracing/binary_carrier.d.ts +0 -11
- package/vendor/dist/opentracing/constants.d.ts +0 -61
- package/vendor/dist/opentracing/examples/demo/demo.d.ts +0 -2
- package/vendor/dist/opentracing/ext/tags.d.ts +0 -90
- package/vendor/dist/opentracing/functions.d.ts +0 -20
- package/vendor/dist/opentracing/global_tracer.d.ts +0 -14
- package/vendor/dist/opentracing/index.d.ts +0 -12
- package/vendor/dist/opentracing/index.js +0 -1
- package/vendor/dist/opentracing/mock_tracer/index.d.ts +0 -5
- package/vendor/dist/opentracing/mock_tracer/mock_context.d.ts +0 -13
- package/vendor/dist/opentracing/mock_tracer/mock_report.d.ts +0 -16
- package/vendor/dist/opentracing/mock_tracer/mock_span.d.ts +0 -50
- package/vendor/dist/opentracing/mock_tracer/mock_tracer.d.ts +0 -26
- package/vendor/dist/opentracing/noop.d.ts +0 -8
- package/vendor/dist/opentracing/reference.d.ts +0 -33
- package/vendor/dist/opentracing/span.d.ts +0 -147
- package/vendor/dist/opentracing/span_context.d.ts +0 -26
- package/vendor/dist/opentracing/test/api_compatibility.d.ts +0 -16
- package/vendor/dist/opentracing/test/mocktracer_implemenation.d.ts +0 -3
- package/vendor/dist/opentracing/test/noop_implementation.d.ts +0 -4
- package/vendor/dist/opentracing/test/opentracing_api.d.ts +0 -3
- package/vendor/dist/opentracing/test/unittest.d.ts +0 -2
- package/vendor/dist/opentracing/tracer.d.ts +0 -127
|
@@ -18,6 +18,7 @@ const {
|
|
|
18
18
|
getIsFaultyEarlyFlakeDetection,
|
|
19
19
|
collectTestOptimizationSummariesFromTraces,
|
|
20
20
|
logTestOptimizationSummary,
|
|
21
|
+
getTestOptimizationRequestResults,
|
|
21
22
|
} = require('../../../dd-trace/src/plugins/util/test')
|
|
22
23
|
|
|
23
24
|
const {
|
|
@@ -109,6 +110,29 @@ function isTestFailed (test) {
|
|
|
109
110
|
return false
|
|
110
111
|
}
|
|
111
112
|
|
|
113
|
+
function getRootSuiteStatus (rootTests) {
|
|
114
|
+
let status = 'pass'
|
|
115
|
+
if (rootTests.every(t => t.isPending())) {
|
|
116
|
+
status = 'skip'
|
|
117
|
+
} else {
|
|
118
|
+
for (const test of rootTests) {
|
|
119
|
+
if (test.state === 'failed' || test.timedOut || test._ddHookFailed) {
|
|
120
|
+
status = 'fail'
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return status
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function haveRootTestsFinished (rootTests) {
|
|
128
|
+
for (const test of rootTests) {
|
|
129
|
+
if (!test.isPending() && !test.state && !test.timedOut && !test._ddHookFailed) {
|
|
130
|
+
return false
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return true
|
|
134
|
+
}
|
|
135
|
+
|
|
112
136
|
function getFilteredSuites (originalSuites) {
|
|
113
137
|
return originalSuites.reduce((acc, suite) => {
|
|
114
138
|
const testPath = getTestSuitePath(suite.file, process.cwd())
|
|
@@ -226,11 +250,38 @@ function getOnEndHandler (isParallel) {
|
|
|
226
250
|
}
|
|
227
251
|
}
|
|
228
252
|
|
|
253
|
+
function getRunStoresPromise (channelToPublishTo, ctx) {
|
|
254
|
+
return new Promise(resolve => {
|
|
255
|
+
channelToPublishTo.runStores({ ...ctx, onDone: resolve }, () => {})
|
|
256
|
+
})
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function applyKnownTestsResponse ({ err, knownTests }) {
|
|
260
|
+
if (err) {
|
|
261
|
+
config.knownTests = []
|
|
262
|
+
config.isEarlyFlakeDetectionEnabled = false
|
|
263
|
+
config.isKnownTestsEnabled = false
|
|
264
|
+
} else {
|
|
265
|
+
config.knownTests = knownTests
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function applyTestManagementTestsResponse ({ err, testManagementTests: receivedTestManagementTests }) {
|
|
270
|
+
if (err) {
|
|
271
|
+
config.testManagementTests = {}
|
|
272
|
+
config.isTestManagementTestsEnabled = false
|
|
273
|
+
config.testManagementAttemptToFixRetries = 0
|
|
274
|
+
} else {
|
|
275
|
+
config.testManagementTests = receivedTestManagementTests
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
229
279
|
function getExecutionConfiguration (runner, isParallel, frameworkVersion, onFinishRequest) {
|
|
230
280
|
const ctx = {
|
|
231
281
|
isParallel,
|
|
232
282
|
frameworkVersion,
|
|
233
283
|
}
|
|
284
|
+
let skippableSuitesResponse
|
|
234
285
|
|
|
235
286
|
const onReceivedSkippableSuites = ({ err, skippableSuites, itrCorrelationId: responseItrCorrelationId }) => {
|
|
236
287
|
if (err) {
|
|
@@ -256,6 +307,16 @@ function getExecutionConfiguration (runner, isParallel, frameworkVersion, onFini
|
|
|
256
307
|
})
|
|
257
308
|
}
|
|
258
309
|
|
|
310
|
+
const requestSkippableSuites = () => {
|
|
311
|
+
if (skippableSuitesResponse) {
|
|
312
|
+
onReceivedSkippableSuites(skippableSuitesResponse)
|
|
313
|
+
return
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
ctx.onDone = onReceivedSkippableSuites
|
|
317
|
+
skippableSuitesCh.runStores(ctx, () => {})
|
|
318
|
+
}
|
|
319
|
+
|
|
259
320
|
const onReceivedImpactedTests = ({ err, modifiedFiles: receivedModifiedFiles }) => {
|
|
260
321
|
if (err) {
|
|
261
322
|
config.modifiedFiles = []
|
|
@@ -264,8 +325,7 @@ function getExecutionConfiguration (runner, isParallel, frameworkVersion, onFini
|
|
|
264
325
|
config.modifiedFiles = receivedModifiedFiles
|
|
265
326
|
}
|
|
266
327
|
if (config.isSuitesSkippingEnabled) {
|
|
267
|
-
|
|
268
|
-
skippableSuitesCh.runStores(ctx, () => {})
|
|
328
|
+
requestSkippableSuites()
|
|
269
329
|
} else {
|
|
270
330
|
mochaGlobalRunCh.runStores(ctx, () => {
|
|
271
331
|
onFinishRequest()
|
|
@@ -273,44 +333,12 @@ function getExecutionConfiguration (runner, isParallel, frameworkVersion, onFini
|
|
|
273
333
|
}
|
|
274
334
|
}
|
|
275
335
|
|
|
276
|
-
const
|
|
277
|
-
if (err) {
|
|
278
|
-
config.testManagementTests = {}
|
|
279
|
-
config.isTestManagementTestsEnabled = false
|
|
280
|
-
config.testManagementAttemptToFixRetries = 0
|
|
281
|
-
} else {
|
|
282
|
-
config.testManagementTests = receivedTestManagementTests
|
|
283
|
-
}
|
|
336
|
+
const continueAfterTestRequests = () => {
|
|
284
337
|
if (config.isImpactedTestsEnabled) {
|
|
285
338
|
ctx.onDone = onReceivedImpactedTests
|
|
286
339
|
modifiedFilesCh.runStores(ctx, () => {})
|
|
287
340
|
} else if (config.isSuitesSkippingEnabled) {
|
|
288
|
-
|
|
289
|
-
skippableSuitesCh.runStores(ctx, () => {})
|
|
290
|
-
} else {
|
|
291
|
-
mochaGlobalRunCh.runStores(ctx, () => {
|
|
292
|
-
onFinishRequest()
|
|
293
|
-
})
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
const onReceivedKnownTests = ({ err, knownTests }) => {
|
|
298
|
-
if (err) {
|
|
299
|
-
config.knownTests = []
|
|
300
|
-
config.isEarlyFlakeDetectionEnabled = false
|
|
301
|
-
config.isKnownTestsEnabled = false
|
|
302
|
-
} else {
|
|
303
|
-
config.knownTests = knownTests
|
|
304
|
-
}
|
|
305
|
-
if (config.isTestManagementTestsEnabled) {
|
|
306
|
-
ctx.onDone = onReceivedTestManagementTests
|
|
307
|
-
testManagementTestsCh.runStores(ctx, () => {})
|
|
308
|
-
} else if (config.isImpactedTestsEnabled) {
|
|
309
|
-
ctx.onDone = onReceivedImpactedTests
|
|
310
|
-
modifiedFilesCh.runStores(ctx, () => {})
|
|
311
|
-
} else if (config.isSuitesSkippingEnabled) {
|
|
312
|
-
ctx.onDone = onReceivedSkippableSuites
|
|
313
|
-
skippableSuitesCh.runStores(ctx, () => {})
|
|
341
|
+
requestSkippableSuites()
|
|
314
342
|
} else {
|
|
315
343
|
mochaGlobalRunCh.runStores(ctx, () => {
|
|
316
344
|
onFinishRequest()
|
|
@@ -336,23 +364,30 @@ function getExecutionConfiguration (runner, isParallel, frameworkVersion, onFini
|
|
|
336
364
|
config.isFlakyTestRetriesEnabled = libraryConfig.isFlakyTestRetriesEnabled
|
|
337
365
|
config.flakyTestRetriesCount = libraryConfig.flakyTestRetriesCount
|
|
338
366
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
367
|
+
getTestOptimizationRequestResults({
|
|
368
|
+
isKnownTestsEnabled: config.isKnownTestsEnabled,
|
|
369
|
+
isTestManagementTestsEnabled: config.isTestManagementTestsEnabled,
|
|
370
|
+
isSuitesSkippingEnabled: config.isSuitesSkippingEnabled,
|
|
371
|
+
getKnownTests: () => getRunStoresPromise(knownTestsCh, ctx),
|
|
372
|
+
getTestManagementTests: () => getRunStoresPromise(testManagementTestsCh, ctx),
|
|
373
|
+
getSkippableSuites: () => getRunStoresPromise(skippableSuitesCh, ctx),
|
|
374
|
+
}).then(requestResults => {
|
|
375
|
+
const {
|
|
376
|
+
knownTestsResponse,
|
|
377
|
+
testManagementTestsResponse,
|
|
378
|
+
skippableSuitesResponse: requestSkippableSuitesResponse,
|
|
379
|
+
} = requestResults
|
|
380
|
+
|
|
381
|
+
if (knownTestsResponse) {
|
|
382
|
+
applyKnownTestsResponse(knownTestsResponse)
|
|
383
|
+
}
|
|
384
|
+
if (testManagementTestsResponse) {
|
|
385
|
+
applyTestManagementTestsResponse(testManagementTestsResponse)
|
|
386
|
+
}
|
|
387
|
+
skippableSuitesResponse = requestSkippableSuitesResponse
|
|
388
|
+
|
|
389
|
+
continueAfterTestRequests()
|
|
390
|
+
})
|
|
356
391
|
}
|
|
357
392
|
|
|
358
393
|
ctx.onDone = onReceivedConfiguration
|
|
@@ -469,26 +504,204 @@ addHook({
|
|
|
469
504
|
}
|
|
470
505
|
|
|
471
506
|
const { suitesByTestFile, numSuitesByTestFile } = getSuitesByTestFile(this.suite)
|
|
507
|
+
// Root-level tests (direct children of root, no describe wrapper) keyed by file.
|
|
508
|
+
// Populated during the root 'suite' event so the normal finish path can include them
|
|
509
|
+
// in mixed-file status calculation.
|
|
510
|
+
const rootTestsByFile = new Map()
|
|
511
|
+
// Counts how many original tests per pure-root file still need their final attempt.
|
|
512
|
+
// Hits zero when the last test's lifecycle completes, triggering the suite finish.
|
|
513
|
+
const rootPendingCountByFile = new Map()
|
|
514
|
+
const rootFinalizationPendingCountByFile = new Map()
|
|
515
|
+
const rootFallbackPendingFiles = new Set()
|
|
516
|
+
const rootFinalizationPendingTests = new WeakSet()
|
|
517
|
+
let pendingRootFinalizations = 0
|
|
518
|
+
let hasEnded = false
|
|
519
|
+
let hasFinishedRun = false
|
|
520
|
+
let endRunner
|
|
521
|
+
|
|
522
|
+
function updateRootTestForFinalAttempt (test) {
|
|
523
|
+
if (!test._retriedTest) return
|
|
524
|
+
|
|
525
|
+
const rootTests = rootTestsByFile.get(test.file)
|
|
526
|
+
if (!rootTests) return
|
|
527
|
+
|
|
528
|
+
const retriedTestIndex = rootTests.indexOf(test._retriedTest)
|
|
529
|
+
if (retriedTestIndex !== -1) {
|
|
530
|
+
rootTests[retriedTestIndex] = test
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
function finishRunIfReady () {
|
|
535
|
+
if (hasFinishedRun) return
|
|
536
|
+
if (hasEnded && pendingRootFinalizations === 0) {
|
|
537
|
+
hasFinishedRun = true
|
|
538
|
+
onEnd.call(endRunner)
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
function incrementPendingRootFinalization (test) {
|
|
543
|
+
if (!rootPendingCountByFile.has(test.file) || rootFinalizationPendingTests.has(test)) return
|
|
544
|
+
|
|
545
|
+
rootFinalizationPendingTests.add(test)
|
|
546
|
+
pendingRootFinalizations++
|
|
547
|
+
rootFinalizationPendingCountByFile.set(
|
|
548
|
+
test.file,
|
|
549
|
+
(rootFinalizationPendingCountByFile.get(test.file) || 0) + 1
|
|
550
|
+
)
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
function decrementPendingRootFinalization (test) {
|
|
554
|
+
if (!rootFinalizationPendingTests.has(test)) return
|
|
555
|
+
|
|
556
|
+
rootFinalizationPendingTests.delete(test)
|
|
557
|
+
pendingRootFinalizations--
|
|
558
|
+
|
|
559
|
+
const remaining = rootFinalizationPendingCountByFile.get(test.file) - 1
|
|
560
|
+
if (remaining > 0) {
|
|
561
|
+
rootFinalizationPendingCountByFile.set(test.file, remaining)
|
|
562
|
+
} else {
|
|
563
|
+
rootFinalizationPendingCountByFile.delete(test.file)
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
if (!rootFinalizationPendingCountByFile.has(test.file) && rootFallbackPendingFiles.delete(test.file)) {
|
|
567
|
+
finishRootSuiteFallbackForFile(test.file)
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
finishRunIfReady()
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
function finishRootSuiteForFile (file) {
|
|
574
|
+
const remaining = rootPendingCountByFile.get(file) - 1
|
|
575
|
+
if (remaining > 0) {
|
|
576
|
+
rootPendingCountByFile.set(file, remaining)
|
|
577
|
+
return
|
|
578
|
+
}
|
|
579
|
+
rootPendingCountByFile.delete(file)
|
|
580
|
+
|
|
581
|
+
const ctx = testFileToSuiteCtx.get(file)
|
|
582
|
+
if (!ctx) {
|
|
583
|
+
log.warn('No ctx found for suite', file)
|
|
584
|
+
return
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
const rootTests = rootTestsByFile.get(file) || []
|
|
588
|
+
const status = getRootSuiteStatus(rootTests)
|
|
589
|
+
|
|
590
|
+
if (global.__coverage__) {
|
|
591
|
+
const coverageFiles = getCoveredFilenamesFromCoverage(global.__coverage__)
|
|
592
|
+
testSuiteCodeCoverageCh.publish({ coverageFiles, suiteFile: file })
|
|
593
|
+
mergeCoverage(global.__coverage__, originalCoverageMap)
|
|
594
|
+
resetCoverage(global.__coverage__)
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
testSuiteFinishCh.publish({ status, ...ctx.currentStore }, () => {})
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
function finishRootSuiteFallbackForFile (file) {
|
|
601
|
+
const ctx = testFileToSuiteCtx.get(file)
|
|
602
|
+
if (!ctx || !rootPendingCountByFile.has(file)) return
|
|
603
|
+
|
|
604
|
+
const rootTests = rootTestsByFile.get(file) || []
|
|
605
|
+
const status = haveRootTestsFinished(rootTests) ? getRootSuiteStatus(rootTests) : 'fail'
|
|
606
|
+
rootPendingCountByFile.delete(file)
|
|
607
|
+
testSuiteFinishCh.publish({ status, ...ctx.currentStore }, () => {})
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
function finishRootSuiteAfterFinalAttempt (test) {
|
|
611
|
+
if (!test._ddIsFinalAttempt || !rootPendingCountByFile.has(test.file)) return
|
|
612
|
+
|
|
613
|
+
updateRootTestForFinalAttempt(test)
|
|
614
|
+
finishRootSuiteForFile(test.file)
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
const onEnd = getOnEndHandler(false)
|
|
472
618
|
|
|
473
619
|
this.once('start', getOnStartHandler(frameworkVersion))
|
|
474
620
|
|
|
475
|
-
this.once('end',
|
|
621
|
+
this.once('end', function () {
|
|
622
|
+
hasEnded = true
|
|
623
|
+
endRunner = this
|
|
624
|
+
finishRunIfReady()
|
|
625
|
+
})
|
|
626
|
+
|
|
627
|
+
// The job of this listener is to
|
|
628
|
+
// initialize the suite span tag in correct order
|
|
629
|
+
// (that is suiteA -> testA ... -> suiteB -> testB
|
|
630
|
+
// instead of suiteA -> suiteB -> testA -> ... -> testB)
|
|
631
|
+
// when the suite has tests that are in the top level
|
|
632
|
+
// (no describe(...))
|
|
633
|
+
this.on('test', function (test) {
|
|
634
|
+
const ctx = testFileToSuiteCtx.get(test.file)
|
|
635
|
+
if (ctx?._pendingRootStart) {
|
|
636
|
+
ctx._pendingRootStart = false
|
|
637
|
+
testSuiteStartCh.runStores(ctx, () => {})
|
|
638
|
+
}
|
|
639
|
+
})
|
|
476
640
|
|
|
477
641
|
this.on('test', getOnTestHandler(true))
|
|
478
642
|
|
|
479
|
-
this.on('test end', getOnTestEndHandler(config
|
|
643
|
+
this.on('test end', getOnTestEndHandler(config, {
|
|
644
|
+
onStart: incrementPendingRootFinalization,
|
|
645
|
+
onFinish: function (test) {
|
|
646
|
+
finishRootSuiteAfterFinalAttempt(test)
|
|
647
|
+
decrementPendingRootFinalization(test)
|
|
648
|
+
},
|
|
649
|
+
}))
|
|
480
650
|
|
|
481
651
|
this.on('retry', getOnTestRetryHandler(config))
|
|
482
652
|
|
|
483
653
|
// If the hook passes, 'hook end' will be emitted. Otherwise, 'fail' will be emitted
|
|
484
654
|
this.on('hook end', getOnHookEndHandler(config))
|
|
485
655
|
|
|
656
|
+
this.on('hook end', function (hook) {
|
|
657
|
+
const test = hook.ctx?.currentTest
|
|
658
|
+
if (!test) return
|
|
659
|
+
finishRootSuiteAfterFinalAttempt(test)
|
|
660
|
+
})
|
|
661
|
+
|
|
486
662
|
this.on('fail', getOnFailHandler(true, config))
|
|
487
663
|
|
|
664
|
+
this.on('fail', function (testOrHook) {
|
|
665
|
+
if (testOrHook.type !== 'hook') return
|
|
666
|
+
const test = testOrHook.ctx?.currentTest
|
|
667
|
+
if (!test) return
|
|
668
|
+
finishRootSuiteAfterFinalAttempt(test)
|
|
669
|
+
})
|
|
670
|
+
|
|
488
671
|
this.on('pending', getOnPendingHandler())
|
|
489
672
|
|
|
490
673
|
this.on('suite', function (suite) {
|
|
491
674
|
if (suite.root || !suite.tests.length) {
|
|
675
|
+
// This branch can be triggered when we have top level it(...) inside test files.
|
|
676
|
+
// In that case, they all (even if they are from different files) are going to be
|
|
677
|
+
// children of the root suite.
|
|
678
|
+
// Note: We could have suites that contain top level it(...) and also it(...) nested
|
|
679
|
+
// inside describe(...) ("mixed case"). Duplication is avoided by the context guard
|
|
680
|
+
// below. Since 'suite' fires for root first, in the mixed case the ctx is created
|
|
681
|
+
// here and the describe-based handler finds it already set.
|
|
682
|
+
if (suite.root && suite.tests.length > 0) {
|
|
683
|
+
const files = new Set(suite.tests.map(test => test.file).filter(Boolean))
|
|
684
|
+
for (const file of files) {
|
|
685
|
+
const testsForFile = suite.tests.filter(t => t.file === file)
|
|
686
|
+
rootTestsByFile.set(file, testsForFile)
|
|
687
|
+
// Only track the countdown for pure root-level files.
|
|
688
|
+
// Mixed files are finished by the normal 'suite end' path.
|
|
689
|
+
if (!suitesByTestFile[file]) {
|
|
690
|
+
rootPendingCountByFile.set(file, testsForFile.length)
|
|
691
|
+
}
|
|
692
|
+
if (testFileToSuiteCtx.get(file)) continue
|
|
693
|
+
const isUnskippable = unskippableSuites.includes(file)
|
|
694
|
+
isForcedToRun = isUnskippable && suitesToSkip.includes(getTestSuitePath(file, process.cwd()))
|
|
695
|
+
const ctx = {
|
|
696
|
+
testSuiteAbsolutePath: file,
|
|
697
|
+
isUnskippable,
|
|
698
|
+
isForcedToRun,
|
|
699
|
+
itrCorrelationId,
|
|
700
|
+
_pendingRootStart: true, // Now the suite start fires lazily on the first test event for this file
|
|
701
|
+
}
|
|
702
|
+
testFileToSuiteCtx.set(file, ctx)
|
|
703
|
+
}
|
|
704
|
+
}
|
|
492
705
|
return
|
|
493
706
|
}
|
|
494
707
|
let ctx = testFileToSuiteCtx.get(suite.file)
|
|
@@ -508,6 +721,40 @@ addHook({
|
|
|
508
721
|
|
|
509
722
|
this.on('suite end', function (suite) {
|
|
510
723
|
if (suite.root) {
|
|
724
|
+
// Normal case: pure root-level files are finished by the 'test end' / 'hook end'
|
|
725
|
+
// listeners via finishRootSuiteForFile. Two edge cases remain here:
|
|
726
|
+
//
|
|
727
|
+
// 1. All-pending: no 'test' event fired, _pendingRootStart is still true.
|
|
728
|
+
// Start and immediately finish with 'skip'.
|
|
729
|
+
//
|
|
730
|
+
// 2. Aborted mid-run (e.g. a beforeEach hook failure): Mocha skips remaining
|
|
731
|
+
// tests and jumps straight to 'suite end'. rootPendingCountByFile still has
|
|
732
|
+
// a nonzero count for the file because the last tests never ran. Finish it
|
|
733
|
+
// as failed now.
|
|
734
|
+
//
|
|
735
|
+
// 3. Async finalization lagged behind Mocha's synchronous events (e.g. DI retry
|
|
736
|
+
// wait): all tests have Mocha terminal state, but the final-attempt callback
|
|
737
|
+
// did not run before root 'suite end'. Finish from the observed test states.
|
|
738
|
+
const processedFiles = new Set()
|
|
739
|
+
for (const test of suite.tests) {
|
|
740
|
+
if (!test.file || processedFiles.has(test.file)) continue
|
|
741
|
+
processedFiles.add(test.file)
|
|
742
|
+
if (suitesByTestFile[test.file]) continue // mixed: handled by normal path
|
|
743
|
+
const ctx = testFileToSuiteCtx.get(test.file)
|
|
744
|
+
if (!ctx) continue
|
|
745
|
+
if (ctx._pendingRootStart) {
|
|
746
|
+
ctx._pendingRootStart = false
|
|
747
|
+
testSuiteStartCh.runStores(ctx, () => {})
|
|
748
|
+
testSuiteFinishCh.publish({ status: 'skip', ...ctx.currentStore }, () => {})
|
|
749
|
+
} else if (rootPendingCountByFile.has(test.file)) {
|
|
750
|
+
if (rootFinalizationPendingCountByFile.has(test.file)) {
|
|
751
|
+
rootFallbackPendingFiles.add(test.file)
|
|
752
|
+
continue
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
finishRootSuiteFallbackForFile(test.file)
|
|
756
|
+
}
|
|
757
|
+
}
|
|
511
758
|
return
|
|
512
759
|
}
|
|
513
760
|
const suitesInTestFile = suitesByTestFile[suite.file]
|
|
@@ -517,8 +764,9 @@ addHook({
|
|
|
517
764
|
return
|
|
518
765
|
}
|
|
519
766
|
|
|
767
|
+
const rootTests = rootTestsByFile.get(suite.file) || []
|
|
520
768
|
let status = 'pass'
|
|
521
|
-
if (suitesInTestFile.every(suite => suite.pending)) {
|
|
769
|
+
if (suitesInTestFile.every(suite => suite.pending) && rootTests.every(test => test.isPending())) {
|
|
522
770
|
status = 'skip'
|
|
523
771
|
} else {
|
|
524
772
|
// has to check every test in the test file
|
|
@@ -530,6 +778,11 @@ addHook({
|
|
|
530
778
|
}
|
|
531
779
|
})
|
|
532
780
|
})
|
|
781
|
+
for (const test of rootTests) {
|
|
782
|
+
if (test.state === 'failed' || test.timedOut) {
|
|
783
|
+
status = 'fail'
|
|
784
|
+
}
|
|
785
|
+
}
|
|
533
786
|
}
|
|
534
787
|
|
|
535
788
|
if (global.__coverage__) {
|
|
@@ -503,13 +503,38 @@ function getTestFinishInfo (test, status, config, error) {
|
|
|
503
503
|
}
|
|
504
504
|
}
|
|
505
505
|
|
|
506
|
-
function getOnTestEndHandler (config) {
|
|
506
|
+
function getOnTestEndHandler (config, finalAttemptHandlers) {
|
|
507
507
|
return async function (test) {
|
|
508
508
|
if (test._ddShouldSkipEfdRetry) {
|
|
509
509
|
return
|
|
510
510
|
}
|
|
511
511
|
const ctx = getTestContext(test)
|
|
512
512
|
const status = getTestStatus(test)
|
|
513
|
+
const shouldFinishTest = ctx && (!getAfterEachHooks(test).length || (test._ddIsDisabled && !test._ddIsAttemptToFix))
|
|
514
|
+
let testFinishInfo
|
|
515
|
+
let isFinalAttempt = false
|
|
516
|
+
|
|
517
|
+
// If there are afterEach to be run, we don't finish the test yet.
|
|
518
|
+
// Disabled tests (marked pending by us) are finished immediately without waiting for afterEach hooks.
|
|
519
|
+
// In older mocha versions, pending tests don't run afterEach hooks, so we can't rely on
|
|
520
|
+
// getOnHookEndHandler to finish the test. This mirrors Jest's approach where the skip handler
|
|
521
|
+
// directly sets finalStatus without waiting for hooks
|
|
522
|
+
if (!ctx && test.isPending()) {
|
|
523
|
+
test._ddIsFinalAttempt = true
|
|
524
|
+
isFinalAttempt = true
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
if (shouldFinishTest) {
|
|
528
|
+
testFinishInfo = getTestFinishInfo(test, status, config, ctx.err || test.err)
|
|
529
|
+
if (testFinishInfo.finalStatus !== undefined) {
|
|
530
|
+
test._ddIsFinalAttempt = true
|
|
531
|
+
isFinalAttempt = true
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
if (isFinalAttempt) {
|
|
536
|
+
finalAttemptHandlers?.onStart?.(test)
|
|
537
|
+
}
|
|
513
538
|
|
|
514
539
|
// After finishing it might take a bit for the snapshot to be handled.
|
|
515
540
|
// This means that tests retried with DI are BREAKPOINT_HIT_GRACE_PERIOD_MS slower at least.
|
|
@@ -521,13 +546,7 @@ function getOnTestEndHandler (config) {
|
|
|
521
546
|
})
|
|
522
547
|
}
|
|
523
548
|
|
|
524
|
-
|
|
525
|
-
// Disabled tests (marked pending by us) are finished immediately without waiting for afterEach hooks.
|
|
526
|
-
// In older mocha versions, pending tests don't run afterEach hooks, so we can't rely on
|
|
527
|
-
// getOnHookEndHandler to finish the test. This mirrors Jest's approach where the skip handler
|
|
528
|
-
// directly sets finalStatus without waiting for hooks
|
|
529
|
-
if (ctx && (!getAfterEachHooks(test).length || (test._ddIsDisabled && !test._ddIsAttemptToFix))) {
|
|
530
|
-
const testFinishInfo = getTestFinishInfo(test, status, config, ctx.err || test.err)
|
|
549
|
+
if (shouldFinishTest) {
|
|
531
550
|
testFinishCh.publish({
|
|
532
551
|
status,
|
|
533
552
|
hasBeenRetried: isMochaRetry(test),
|
|
@@ -536,6 +555,10 @@ function getOnTestEndHandler (config) {
|
|
|
536
555
|
...ctx.currentStore,
|
|
537
556
|
})
|
|
538
557
|
}
|
|
558
|
+
|
|
559
|
+
if (isFinalAttempt) {
|
|
560
|
+
finalAttemptHandlers?.onFinish?.(test)
|
|
561
|
+
}
|
|
539
562
|
}
|
|
540
563
|
}
|
|
541
564
|
|
|
@@ -552,6 +575,9 @@ function getOnHookEndHandler (config) {
|
|
|
552
575
|
// skip to avoid double-publishing
|
|
553
576
|
if (ctx && (!test._ddIsDisabled || test._ddIsAttemptToFix)) {
|
|
554
577
|
const testFinishInfo = getTestFinishInfo(test, status, config, ctx.err || test.err)
|
|
578
|
+
if (testFinishInfo.finalStatus !== undefined) {
|
|
579
|
+
test._ddIsFinalAttempt = true
|
|
580
|
+
}
|
|
555
581
|
testFinishCh.publish({
|
|
556
582
|
status,
|
|
557
583
|
hasBeenRetried: isMochaRetry(test),
|
|
@@ -583,6 +609,20 @@ function getOnFailHandler (isMain, config) {
|
|
|
583
609
|
testContext.err = err
|
|
584
610
|
errorCh.runStores(testContext, () => {})
|
|
585
611
|
const testFinishInfo = getTestFinishInfo(test, 'fail', config, err)
|
|
612
|
+
// ATR never retries hook failures: this.retries(N) is set in runnableWrapper
|
|
613
|
+
// which only runs when the test function executes — hooks bypass that path,
|
|
614
|
+
// so _retries stays at -1 and getIsLastRetry returns false, leaving finalStatus
|
|
615
|
+
// undefined. We must also mark the attempt final when no clone-based retry
|
|
616
|
+
// mechanism (EFD original, EFD clone, ATF) has queued further attempts.
|
|
617
|
+
const noCloneRetries = !test._ddIsEfdRetry &&
|
|
618
|
+
!((test._ddIsNew || test._ddIsModified) && config.isEarlyFlakeDetectionEnabled) &&
|
|
619
|
+
!test._ddIsAttemptToFix
|
|
620
|
+
if (testFinishInfo.finalStatus !== undefined || noCloneRetries) {
|
|
621
|
+
test._ddIsFinalAttempt = true
|
|
622
|
+
}
|
|
623
|
+
// test.state is never set to 'failed' for hook failures (Mocha marks the hook,
|
|
624
|
+
// not the test). Flag it so finishRootSuiteForFile can compute the correct status.
|
|
625
|
+
test._ddHookFailed = true
|
|
586
626
|
testFinishCh.publish({
|
|
587
627
|
status: 'fail',
|
|
588
628
|
hasBeenRetried: isMochaRetry(test),
|
|
@@ -11,6 +11,14 @@ const startCh = channel('apm:mongodb:query:start')
|
|
|
11
11
|
const finishCh = channel('apm:mongodb:query:finish')
|
|
12
12
|
const errorCh = channel('apm:mongodb:query:error')
|
|
13
13
|
|
|
14
|
+
// Per-Connection cached topology shape (mongodb >= 4). The Connection's `address` is immutable
|
|
15
|
+
// for the lifetime of the connection, so we synthesize the `{ s: { options } }` envelope the
|
|
16
|
+
// plugin expects only once per connection. A WeakMap keeps the cache off the foreign Connection
|
|
17
|
+
// instance — no extra own-key visible to `Reflect.ownKeys`, `Object.freeze`, or another tracer's
|
|
18
|
+
// instrumentation walking the connection.
|
|
19
|
+
/** @type {WeakMap<object, { s: { options: { host?: string, port?: string } } }>} */
|
|
20
|
+
const topologyCache = new WeakMap()
|
|
21
|
+
|
|
14
22
|
addHook({ name: 'mongodb-core', versions: ['2 - 3.1.9'] }, Server => {
|
|
15
23
|
const serverProto = Server.Server.prototype
|
|
16
24
|
shimmer.wrap(serverProto, 'command', command => wrapCommand(command, 'command'))
|
|
@@ -88,21 +96,36 @@ function wrapUnifiedCommand (command, operation, name) {
|
|
|
88
96
|
}
|
|
89
97
|
|
|
90
98
|
function wrapConnectionCommand (command, operation, name, instrumentFn = instrument) {
|
|
99
|
+
const opts = { name }
|
|
91
100
|
return function (ns, ops) {
|
|
92
101
|
if (!startCh.hasSubscribers) {
|
|
93
102
|
return command.apply(this, arguments)
|
|
94
103
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
ns = `${ns.db}.${ns.collection}`
|
|
102
|
-
return instrumentFn(operation, command, this, arguments, topology, ns, ops, { name })
|
|
104
|
+
let topology = topologyCache.get(this)
|
|
105
|
+
if (topology === undefined) {
|
|
106
|
+
topology = synthesizeTopology(this.address)
|
|
107
|
+
topologyCache.set(this, topology)
|
|
108
|
+
}
|
|
109
|
+
return instrumentFn(operation, command, this, arguments, topology, `${ns.db}.${ns.collection}`, ops, opts)
|
|
103
110
|
}
|
|
104
111
|
}
|
|
105
112
|
|
|
113
|
+
/**
|
|
114
|
+
* @param {string} address
|
|
115
|
+
* @returns {{ s: { options: { host?: string, port?: string } } }}
|
|
116
|
+
*/
|
|
117
|
+
function synthesizeTopology (address) {
|
|
118
|
+
if (typeof address === 'string') {
|
|
119
|
+
const colon = address.indexOf(':')
|
|
120
|
+
// Match the previous `.split(':')` length-2 check: exactly one colon with non-empty parts on both sides.
|
|
121
|
+
if (colon > 0 && colon < address.length - 1 && !address.includes(':', colon + 1)) {
|
|
122
|
+
return { s: { options: { host: address.slice(0, colon), port: address.slice(colon + 1) } } }
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// No port means the address is a random UUID, an IPv6 form, or otherwise unparseable, so no host either.
|
|
126
|
+
return { s: { options: {} } }
|
|
127
|
+
}
|
|
128
|
+
|
|
106
129
|
function wrapQuery (query, operation, name) {
|
|
107
130
|
return function (...args) {
|
|
108
131
|
if (!startCh.hasSubscribers) {
|
|
@@ -151,7 +174,7 @@ function instrument (operation, command, instance, args, server, ns, ops, option
|
|
|
151
174
|
name,
|
|
152
175
|
}
|
|
153
176
|
return startCh.runStores(ctx, () => {
|
|
154
|
-
args[index] = shimmer.
|
|
177
|
+
args[index] = shimmer.wrapCallback(callback, callback => function (err, res) {
|
|
155
178
|
if (err) {
|
|
156
179
|
ctx.error = err
|
|
157
180
|
errorCh.publish(ctx)
|
|
@@ -171,6 +194,8 @@ function instrument (operation, command, instance, args, server, ns, ops, option
|
|
|
171
194
|
})
|
|
172
195
|
}
|
|
173
196
|
|
|
197
|
+
module.exports = { synthesizeTopology }
|
|
198
|
+
|
|
174
199
|
function instrumentPromise (operation, command, instance, args, server, ns, ops, options = {}) {
|
|
175
200
|
const name = options.name || (ops && Object.keys(ops)[0])
|
|
176
201
|
|
|
@@ -125,21 +125,19 @@ addHook({
|
|
|
125
125
|
const resolve = args[0]
|
|
126
126
|
const reject = args[1]
|
|
127
127
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
if (resolve) {
|
|
128
|
+
if (typeof resolve === 'function') {
|
|
129
|
+
args[0] = shimmer.wrapCallback(resolve, resolve => function wrappedResolve (...args) {
|
|
130
|
+
finishCh.publish(ctx)
|
|
132
131
|
return resolve.apply(this, args)
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
args[1] = shimmer.wrapFunction(reject, reject => function wrappedReject (...args) {
|
|
137
|
-
finishCh.publish(ctx)
|
|
132
|
+
})
|
|
133
|
+
}
|
|
138
134
|
|
|
139
|
-
|
|
135
|
+
if (typeof reject === 'function') {
|
|
136
|
+
args[1] = shimmer.wrapCallback(reject, reject => function wrappedReject (...args) {
|
|
137
|
+
finishCh.publish(ctx)
|
|
140
138
|
return reject.apply(this, args)
|
|
141
|
-
}
|
|
142
|
-
}
|
|
139
|
+
})
|
|
140
|
+
}
|
|
143
141
|
|
|
144
142
|
return originalThen.apply(this, args)
|
|
145
143
|
}
|