dd-trace 5.69.0 → 5.71.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 CHANGED
@@ -168,6 +168,7 @@ interface Plugins {
168
168
  "aerospike": tracer.plugins.aerospike;
169
169
  "amqp10": tracer.plugins.amqp10;
170
170
  "amqplib": tracer.plugins.amqplib;
171
+ "anthropic": tracer.plugins.anthropic;
171
172
  "apollo": tracer.plugins.apollo;
172
173
  "avsc": tracer.plugins.avsc;
173
174
  "aws-sdk": tracer.plugins.aws_sdk;
@@ -785,6 +786,8 @@ declare namespace tracer {
785
786
 
786
787
  /** Whether to enable request body collection on RASP event
787
788
  * @default false
789
+ *
790
+ * @deprecated Use UI and Remote Configuration to enable extended data collection
788
791
  */
789
792
  bodyCollection?: boolean
790
793
  },
@@ -809,20 +812,28 @@ declare namespace tracer {
809
812
  },
810
813
  /**
811
814
  * Configuration for extended headers collection tied to security events
815
+ *
816
+ * @deprecated Use UI and Remote Configuration to enable extended data collection
812
817
  */
813
818
  extendedHeadersCollection?: {
814
819
  /** Whether to enable extended headers collection
815
820
  * @default false
821
+ *
822
+ * @deprecated Use UI and Remote Configuration to enable extended data collection
816
823
  */
817
824
  enabled: boolean,
818
825
 
819
826
  /** Whether to redact collected headers
820
827
  * @default true
828
+ *
829
+ * @deprecated Use UI and Remote Configuration to enable extended data collection
821
830
  */
822
831
  redaction: boolean,
823
832
 
824
833
  /** Specifies the maximum number of headers collected.
825
834
  * @default 50
835
+ *
836
+ * @deprecated Use UI and Remote Configuration to enable extended data collection
826
837
  */
827
838
  maxHeaders: number,
828
839
  }
@@ -1530,6 +1541,12 @@ declare namespace tracer {
1530
1541
  */
1531
1542
  interface amqplib extends Instrumentation {}
1532
1543
 
1544
+ /**
1545
+ * This plugin automatically instruments the
1546
+ * [anthropic](https://www.npmjs.com/package/@anthropic-ai/sdk) module.
1547
+ */
1548
+ interface anthropic extends Instrumentation {}
1549
+
1533
1550
  /**
1534
1551
  * Currently this plugin automatically instruments
1535
1552
  * [@apollo/gateway](https://github.com/apollographql/federation) for module versions >= v2.3.0.
package/initialize.mjs CHANGED
@@ -36,7 +36,13 @@ ${result.source}`
36
36
  const [NODE_MAJOR, NODE_MINOR] = process.versions.node.split('.').map(Number)
37
37
 
38
38
  const brokenLoaders = NODE_MAJOR === 18 && NODE_MINOR === 0
39
- const iitmExclusions = [/langsmith/, /openai\/_shims/, /openai\/resources\/chat\/completions\/messages/, /openai\/agents-core\/dist\/shims/]
39
+ const iitmExclusions = [
40
+ /langsmith/,
41
+ /openai\/_shims/,
42
+ /openai\/resources\/chat\/completions\/messages/,
43
+ /openai\/agents-core\/dist\/shims/,
44
+ /@anthropic-ai\/sdk\/_shims/
45
+ ]
40
46
 
41
47
  export async function load (url, context, nextLoad) {
42
48
  const iitmExclusionsMatch = iitmExclusions.some((exclusion) => exclusion.test(url))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dd-trace",
3
- "version": "5.69.0",
3
+ "version": "5.71.0",
4
4
  "description": "Datadog APM tracing client for JavaScript",
5
5
  "main": "index.js",
6
6
  "typings": "index.d.ts",
@@ -61,7 +61,7 @@
61
61
  "test:integration:playwright": "mocha --timeout 60000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/playwright/*.spec.js\"",
62
62
  "test:integration:selenium": "mocha --timeout 60000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/selenium/*.spec.js\"",
63
63
  "test:integration:vitest": "mocha --timeout 60000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/vitest/*.spec.js\"",
64
- "test:integration:testopt": "mocha --timeout 60000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/ci-visibility/*.spec.js\"",
64
+ "test:integration:testopt": "mocha --timeout 90000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/ci-visibility/*.spec.js\"",
65
65
  "test:integration:profiler": "mocha --timeout 180000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/profiler/*.spec.js\"",
66
66
  "test:integration:plugins": "mocha -r \"packages/dd-trace/test/setup/mocha.js\" \"packages/datadog-plugin-@($(echo $PLUGINS))/test/integration-test/**/*.spec.js\"",
67
67
  "test:unit:plugins": "mocha -r \"packages/dd-trace/test/setup/mocha.js\" \"packages/datadog-instrumentations/test/@($(echo $PLUGINS)).spec.js\" \"packages/datadog-plugin-@($(echo $PLUGINS))/test/**/*.spec.js\" --exclude \"packages/datadog-plugin-@($(echo $PLUGINS))/test/integration-test/**/*.spec.js\"",
@@ -0,0 +1,115 @@
1
+ 'use strict'
2
+
3
+ const { addHook } = require('./helpers/instrument')
4
+ const shimmer = require('../../datadog-shimmer')
5
+ const { channel, tracingChannel } = require('dc-polyfill')
6
+
7
+ const anthropicTracingChannel = tracingChannel('apm:anthropic:request')
8
+ const onStreamedChunkCh = channel('apm:anthropic:request:chunk')
9
+
10
+ function wrapStreamIterator (iterator, ctx) {
11
+ return function () {
12
+ const itr = iterator.apply(this, arguments)
13
+ shimmer.wrap(itr, 'next', next => function () {
14
+ return next.apply(this, arguments)
15
+ .then(res => {
16
+ const { done, value: chunk } = res
17
+ onStreamedChunkCh.publish({ ctx, chunk, done })
18
+
19
+ if (done) {
20
+ finish(ctx)
21
+ }
22
+
23
+ return res
24
+ })
25
+ .catch(error => {
26
+ finish(ctx, null, error)
27
+ throw error
28
+ })
29
+ })
30
+
31
+ return itr
32
+ }
33
+ }
34
+
35
+ function wrapCreate (create) {
36
+ return function () {
37
+ if (!anthropicTracingChannel.start.hasSubscribers) {
38
+ return create.apply(this, arguments)
39
+ }
40
+
41
+ const options = arguments[0]
42
+ const stream = options.stream
43
+
44
+ const ctx = { options, resource: 'create' }
45
+
46
+ return anthropicTracingChannel.start.runStores(ctx, () => {
47
+ let apiPromise
48
+ try {
49
+ apiPromise = create.apply(this, arguments)
50
+ } catch (error) {
51
+ finish(ctx, null, error)
52
+ throw error
53
+ }
54
+
55
+ shimmer.wrap(apiPromise, 'parse', parse => function () {
56
+ return parse.apply(this, arguments)
57
+ .then(response => {
58
+ if (stream) {
59
+ shimmer.wrap(response, Symbol.asyncIterator, iterator => wrapStreamIterator(iterator, ctx))
60
+ } else {
61
+ finish(ctx, response, null)
62
+ }
63
+
64
+ return response
65
+ }).catch(error => {
66
+ finish(ctx, null, error)
67
+ throw error
68
+ })
69
+ })
70
+
71
+ anthropicTracingChannel.end.publish(ctx)
72
+
73
+ return apiPromise
74
+ })
75
+ }
76
+ }
77
+
78
+ function finish (ctx, result, error) {
79
+ if (error) {
80
+ ctx.error = error
81
+ anthropicTracingChannel.error.publish(ctx)
82
+ }
83
+
84
+ // streamed responses are handled and set separately
85
+ ctx.result ??= result
86
+
87
+ anthropicTracingChannel.asyncEnd.publish(ctx)
88
+ }
89
+
90
+ const extensions = ['js', 'mjs']
91
+ for (const extension of extensions) {
92
+ addHook({
93
+ name: '@anthropic-ai/sdk',
94
+ file: `resources/messages.${extension}`,
95
+ versions: ['>=0.14.0 <0.33.0']
96
+ }, exports => {
97
+ const Messages = exports.Messages
98
+
99
+ shimmer.wrap(Messages.prototype, 'create', wrapCreate)
100
+
101
+ return exports
102
+ })
103
+
104
+ addHook({
105
+ name: '@anthropic-ai/sdk',
106
+ file: `resources/messages/messages.${extension}`,
107
+ versions: ['>=0.33.0']
108
+ }, exports => {
109
+ const Messages = exports.Messages
110
+
111
+ shimmer.wrap(Messages.prototype, 'create', wrapCreate)
112
+
113
+ return exports
114
+ })
115
+ }
@@ -1,6 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  module.exports = {
4
+ '@anthropic-ai/sdk': { esmFirst: true, fn: () => require('../anthropic') },
4
5
  '@apollo/server': () => require('../apollo-server'),
5
6
  '@apollo/gateway': () => require('../apollo'),
6
7
  'apollo-server-core': () => require('../apollo-server-core'),
@@ -11,12 +11,8 @@ const {
11
11
  getTestLineStart,
12
12
  getTestSuitePath,
13
13
  getTestParametersString,
14
- addEfdStringToTestName,
15
- removeEfdStringFromTestName,
16
14
  getIsFaultyEarlyFlakeDetection,
17
15
  JEST_WORKER_LOGS_PAYLOAD_CODE,
18
- addAttemptToFixStringToTestName,
19
- removeAttemptToFixStringFromTestName,
20
16
  getTestEndLine,
21
17
  isModifiedTest
22
18
  } = require('../../dd-trace/src/plugins/util/test')
@@ -92,6 +88,7 @@ const newTestsTestStatuses = new Map()
92
88
  const attemptToFixRetriedTestsStatuses = new Map()
93
89
  const wrappedWorkers = new WeakSet()
94
90
  const testSuiteMockedFiles = new Map()
91
+ const testsToBeRetried = new Set()
95
92
 
96
93
  const BREAKPOINT_HIT_GRACE_PERIOD_MS = 200
97
94
 
@@ -146,6 +143,8 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
146
143
 
147
144
  const repositoryRoot = this.testEnvironmentOptions._ddRepositoryRoot
148
145
 
146
+ // TODO: could we grab testPath from `this.getVmContext().expect.getState()` instead?
147
+ // so we don't rely on context being passed (some custom test environment do not pass it)
149
148
  if (repositoryRoot) {
150
149
  this.testSourceFile = getTestSuitePath(context.testPath, repositoryRoot)
151
150
  this.repositoryRoot = repositoryRoot
@@ -209,19 +208,31 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
209
208
  }
210
209
  }
211
210
 
212
- getHasSnapshotTests () {
213
- if (this.hasSnapshotTests !== undefined) {
214
- return this.hasSnapshotTests
215
- }
216
- let hasSnapshotTests = true
211
+ /**
212
+ * Jest snapshot counter issue during test retries
213
+ *
214
+ * Problem:
215
+ * - Jest tracks snapshot calls using an internal counter per test name
216
+ * - Each `toMatchSnapshot()` call increments this counter
217
+ * - When a test is retried, it keeps the same name but the counter continues from where it left off
218
+ *
219
+ * Example Issue:
220
+ * Original test run creates: `exports["test can do multiple snapshots 1"] = "hello"`
221
+ * Retried test expects: `exports["test can do multiple snapshots 2"] = "hello"`
222
+ *
223
+ * This mismatch causes snapshot tests to fail on retry because Jest is looking
224
+ * for the wrong snapshot number. The solution is to reset the snapshot state.
225
+ */
226
+ resetSnapshotState () {
217
227
  try {
218
- const { _snapshotData } = this.getVmContext().expect.getState().snapshotState
219
- hasSnapshotTests = Object.keys(_snapshotData).length > 0
220
- } catch {
221
- // if we can't be sure, we'll err on the side of caution and assume it has snapshots
228
+ const expectGlobal = this.getVmContext().expect
229
+ const { snapshotState: { _counters: counters } } = expectGlobal.getState()
230
+ if (counters) {
231
+ counters.clear()
232
+ }
233
+ } catch (e) {
234
+ log.warn('Error resetting snapshot state', e)
222
235
  }
223
- this.hasSnapshotTests = hasSnapshotTests
224
- return hasSnapshotTests
225
236
  }
226
237
 
227
238
  // This function returns an array if the known tests are valid and null otherwise.
@@ -293,18 +304,15 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
293
304
  }
294
305
 
295
306
  // Generic function to handle test retries
296
- retryTest (testName, retryCount, addRetryStringToTestName, retryType, event) {
297
- // Retrying snapshots has proven to be problematic, so we'll skip them for now
298
- // We'll still detect new tests, but we won't retry them.
299
- // TODO: do not bail out of retrying tests for the whole test suite
300
- if (this.getHasSnapshotTests()) {
301
- log.warn('%s is disabled for suites with snapshots', retryType)
302
- return
303
- }
304
-
307
+ retryTest ({
308
+ jestEvent,
309
+ retryCount,
310
+ retryType
311
+ }) {
312
+ const { testName, fn, timeout } = jestEvent
305
313
  for (let retryIndex = 0; retryIndex < retryCount; retryIndex++) {
306
314
  if (this.global.test) {
307
- this.global.test(addRetryStringToTestName(testName, retryIndex), event.fn, event.timeout)
315
+ this.global.test(testName, fn, timeout)
308
316
  } else {
309
317
  log.error('%s could not retry test because global.test is undefined', retryType)
310
318
  }
@@ -314,8 +322,7 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
314
322
  // At the `add_test` event we don't have the test object yet, so we can't use it
315
323
  getTestNameFromAddTestEvent (event, state) {
316
324
  const describeSuffix = getJestTestName(state.currentDescribeBlock)
317
- const fullTestName = describeSuffix ? `${describeSuffix} ${event.testName}` : event.testName
318
- return removeAttemptToFixStringFromTestName(removeEfdStringFromTestName(fullTestName))
325
+ return describeSuffix ? `${describeSuffix} ${event.testName}` : event.testName
319
326
  }
320
327
 
321
328
  async handleTestEvent (event, state) {
@@ -337,25 +344,27 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
337
344
  })
338
345
  }
339
346
  if (event.name === 'test_start') {
347
+ const testName = getJestTestName(event.test)
348
+ if (testsToBeRetried.has(testName)) {
349
+ // This is needed because we're trying tests with the same name
350
+ this.resetSnapshotState()
351
+ }
352
+
340
353
  let isNewTest = false
341
354
  let numEfdRetry = null
342
355
  let numOfAttemptsToFixRetries = null
343
356
  const testParameters = getTestParametersString(this.nameToParams, event.test.name)
344
- // Async resource for this test is created here
345
- // It is used later on by the test_done handler
346
- const testName = getJestTestName(event.test)
347
- const originalTestName = removeEfdStringFromTestName(removeAttemptToFixStringFromTestName(testName))
348
357
 
349
358
  let isAttemptToFix = false
350
359
  let isDisabled = false
351
360
  let isQuarantined = false
352
361
  if (this.isTestManagementTestsEnabled) {
353
- isAttemptToFix = this.testManagementTestsForThisSuite?.attemptToFix?.includes(originalTestName)
354
- isDisabled = this.testManagementTestsForThisSuite?.disabled?.includes(originalTestName)
355
- isQuarantined = this.testManagementTestsForThisSuite?.quarantined?.includes(originalTestName)
362
+ isAttemptToFix = this.testManagementTestsForThisSuite?.attemptToFix?.includes(testName)
363
+ isDisabled = this.testManagementTestsForThisSuite?.disabled?.includes(testName)
364
+ isQuarantined = this.testManagementTestsForThisSuite?.quarantined?.includes(testName)
356
365
  if (isAttemptToFix) {
357
- numOfAttemptsToFixRetries = retriedTestsToNumAttempts.get(originalTestName)
358
- retriedTestsToNumAttempts.set(originalTestName, numOfAttemptsToFixRetries + 1)
366
+ numOfAttemptsToFixRetries = retriedTestsToNumAttempts.get(testName)
367
+ retriedTestsToNumAttempts.set(testName, numOfAttemptsToFixRetries + 1)
359
368
  } else if (isDisabled) {
360
369
  event.test.mode = 'skip'
361
370
  }
@@ -375,17 +384,17 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
375
384
  }
376
385
 
377
386
  if (this.isKnownTestsEnabled) {
378
- isNewTest = retriedTestsToNumAttempts.has(originalTestName)
387
+ isNewTest = retriedTestsToNumAttempts.has(testName)
379
388
  }
380
389
 
381
390
  if (this.isEarlyFlakeDetectionEnabled && (isNewTest || isModified)) {
382
- numEfdRetry = retriedTestsToNumAttempts.get(originalTestName)
383
- retriedTestsToNumAttempts.set(originalTestName, numEfdRetry + 1)
391
+ numEfdRetry = retriedTestsToNumAttempts.get(testName)
392
+ retriedTestsToNumAttempts.set(testName, numEfdRetry + 1)
384
393
  }
385
394
 
386
395
  const isJestRetry = event.test?.invocations > 1
387
396
  const ctx = {
388
- name: originalTestName,
397
+ name: testName,
389
398
  suite: this.testSuite,
390
399
  testSourceFile: this.testSourceFile,
391
400
  displayName: this.displayName,
@@ -431,24 +440,22 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
431
440
  }
432
441
 
433
442
  if (event.name === 'add_test') {
434
- const originalTestName = this.getTestNameFromAddTestEvent(event, state)
435
-
436
443
  if (event.failing) {
437
444
  return
438
445
  }
439
446
 
447
+ const testFullName = this.getTestNameFromAddTestEvent(event, state)
440
448
  const isSkipped = event.mode === 'todo' || event.mode === 'skip'
441
449
  if (this.isTestManagementTestsEnabled) {
442
- const isAttemptToFix = this.testManagementTestsForThisSuite?.attemptToFix?.includes(originalTestName)
443
- if (isAttemptToFix && !isSkipped && !retriedTestsToNumAttempts.has(originalTestName)) {
444
- retriedTestsToNumAttempts.set(originalTestName, 0)
445
- this.retryTest(
446
- event.testName,
447
- testManagementAttemptToFixRetries,
448
- addAttemptToFixStringToTestName,
449
- 'Test Management (Attempt to Fix)',
450
- event
451
- )
450
+ const isAttemptToFix = this.testManagementTestsForThisSuite?.attemptToFix?.includes(testFullName)
451
+ if (isAttemptToFix && !isSkipped && !retriedTestsToNumAttempts.has(testFullName)) {
452
+ retriedTestsToNumAttempts.set(testFullName, 0)
453
+ testsToBeRetried.add(testFullName)
454
+ this.retryTest({
455
+ jestEvent: event,
456
+ retryCount: testManagementAttemptToFixRetries,
457
+ retryType: 'Test Management (Attempt to Fix)'
458
+ })
452
459
  }
453
460
  }
454
461
  if (this.isImpactedTestsEnabled) {
@@ -461,29 +468,27 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
461
468
  this.modifiedTestsForThisSuite,
462
469
  'jest'
463
470
  )
464
- if (isModified && !retriedTestsToNumAttempts.has(originalTestName) && this.isEarlyFlakeDetectionEnabled) {
465
- retriedTestsToNumAttempts.set(originalTestName, 0)
466
- this.retryTest(
467
- event.testName,
468
- earlyFlakeDetectionNumRetries,
469
- addEfdStringToTestName,
470
- 'Early flake detection',
471
- event
472
- )
471
+ if (isModified && !retriedTestsToNumAttempts.has(testFullName) && this.isEarlyFlakeDetectionEnabled) {
472
+ retriedTestsToNumAttempts.set(testFullName, 0)
473
+ testsToBeRetried.add(testFullName)
474
+ this.retryTest({
475
+ jestEvent: event,
476
+ retryCount: earlyFlakeDetectionNumRetries,
477
+ retryType: 'Impacted tests'
478
+ })
473
479
  }
474
480
  }
475
481
  if (this.isKnownTestsEnabled) {
476
- const isNew = !this.knownTestsForThisSuite.includes(originalTestName)
477
- if (isNew && !isSkipped && !retriedTestsToNumAttempts.has(originalTestName)) {
478
- retriedTestsToNumAttempts.set(originalTestName, 0)
482
+ const isNew = !this.knownTestsForThisSuite.includes(testFullName)
483
+ if (isNew && !isSkipped && !retriedTestsToNumAttempts.has(testFullName)) {
484
+ retriedTestsToNumAttempts.set(testFullName, 0)
479
485
  if (this.isEarlyFlakeDetectionEnabled) {
480
- this.retryTest(
481
- event.testName,
482
- earlyFlakeDetectionNumRetries,
483
- addEfdStringToTestName,
484
- 'Early flake detection',
485
- event
486
- )
486
+ testsToBeRetried.add(testFullName)
487
+ this.retryTest({
488
+ jestEvent: event,
489
+ retryCount: earlyFlakeDetectionNumRetries,
490
+ retryType: 'Early flake detection'
491
+ })
487
492
  }
488
493
  }
489
494
  }
@@ -502,15 +507,14 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
502
507
  let isAttemptToFix = false
503
508
  if (this.isTestManagementTestsEnabled) {
504
509
  const testName = getJestTestName(event.test)
505
- const originalTestName = removeAttemptToFixStringFromTestName(testName)
506
- isAttemptToFix = this.testManagementTestsForThisSuite?.attemptToFix?.includes(originalTestName)
510
+ isAttemptToFix = this.testManagementTestsForThisSuite?.attemptToFix?.includes(testName)
507
511
  if (isAttemptToFix) {
508
- if (attemptToFixRetriedTestsStatuses.has(originalTestName)) {
509
- attemptToFixRetriedTestsStatuses.get(originalTestName).push(status)
512
+ if (attemptToFixRetriedTestsStatuses.has(testName)) {
513
+ attemptToFixRetriedTestsStatuses.get(testName).push(status)
510
514
  } else {
511
- attemptToFixRetriedTestsStatuses.set(originalTestName, [status])
515
+ attemptToFixRetriedTestsStatuses.set(testName, [status])
512
516
  }
513
- const testStatuses = attemptToFixRetriedTestsStatuses.get(originalTestName)
517
+ const testStatuses = attemptToFixRetriedTestsStatuses.get(testName)
514
518
  // Check if this is the last attempt to fix.
515
519
  // If it is, we'll set the failedAllTests flag to true if all the tests failed
516
520
  // If all tests passed, we'll set the attemptToFixPassed flag to true
@@ -531,14 +535,13 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
531
535
  // We'll store the test statuses of the retries
532
536
  if (this.isKnownTestsEnabled) {
533
537
  const testName = getJestTestName(event.test)
534
- const originalTestName = removeEfdStringFromTestName(testName)
535
- const isNewTest = retriedTestsToNumAttempts.has(originalTestName)
538
+ const isNewTest = retriedTestsToNumAttempts.has(testName)
536
539
  if (isNewTest) {
537
- if (newTestsTestStatuses.has(originalTestName)) {
538
- newTestsTestStatuses.get(originalTestName).push(status)
540
+ if (newTestsTestStatuses.has(testName)) {
541
+ newTestsTestStatuses.get(testName).push(status)
539
542
  isEfdRetry = true
540
543
  } else {
541
- newTestsTestStatuses.set(originalTestName, [status])
544
+ newTestsTestStatuses.set(testName, [status])
542
545
  }
543
546
  }
544
547
  }
@@ -951,13 +954,12 @@ function getCliWrapper (isNewJestVersion) {
951
954
 
952
955
  for (const { testName, testSuiteAbsolutePath } of failedTests) {
953
956
  const testSuite = getTestSuitePath(testSuiteAbsolutePath, result.globalConfig.rootDir)
954
- const originalName = removeAttemptToFixStringFromTestName(testName)
955
957
  const testManagementTest = testManagementTests
956
958
  ?.jest
957
959
  ?.suites
958
960
  ?.[testSuite]
959
961
  ?.tests
960
- ?.[originalName]
962
+ ?.[testName]
961
963
  ?.properties
962
964
  // This uses `attempt_to_fix` because this is always the main process and it's not formatted in camelCase
963
965
  if (testManagementTest?.attempt_to_fix && (testManagementTest?.quarantined || testManagementTest?.disabled)) {
@@ -1,12 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const {
4
- getTestSuitePath,
5
- removeEfdStringFromTestName,
6
- addEfdStringToTestName,
7
- addAttemptToFixStringToTestName,
8
- removeAttemptToFixStringFromTestName
9
- } = require('../../../dd-trace/src/plugins/util/test')
3
+ const { getTestSuitePath } = require('../../../dd-trace/src/plugins/util/test')
10
4
  const { channel } = require('../helpers/instrument')
11
5
  const shimmer = require('../../../datadog-shimmer')
12
6
 
@@ -60,17 +54,15 @@ function isNewTest (test, knownTests) {
60
54
  return false
61
55
  }
62
56
  const testSuite = getTestSuitePath(test.file, process.cwd())
63
- const testName = removeEfdStringFromTestName(test.fullTitle())
57
+ const testName = test.fullTitle()
64
58
  const testsForSuite = knownTests.mocha?.[testSuite] || []
65
59
  return !testsForSuite.includes(testName)
66
60
  }
67
61
 
68
- function retryTest (test, numRetries, modifyTestName, tags) {
69
- const originalTestName = test.title
62
+ function retryTest (test, numRetries, tags) {
70
63
  const suite = test.parent
71
64
  for (let retryIndex = 0; retryIndex < numRetries; retryIndex++) {
72
65
  const clonedTest = test.clone()
73
- clonedTest.title = modifyTestName(originalTestName, retryIndex + 1)
74
66
  suite.addTest(clonedTest)
75
67
  tags.forEach(tag => {
76
68
  if (tag) {
@@ -113,10 +105,7 @@ function getIsLastRetry (test) {
113
105
  }
114
106
 
115
107
  function getTestFullName (test) {
116
- const testName = removeEfdStringFromTestName(
117
- removeAttemptToFixStringFromTestName(test.fullTitle())
118
- )
119
- return `mocha.${getTestSuitePath(test.file, process.cwd())}.${testName}`
108
+ return `mocha.${getTestSuitePath(test.file, process.cwd())}.${test.fullTitle()}`
120
109
  }
121
110
 
122
111
  function getTestStatus (test) {
@@ -216,10 +205,8 @@ function getOnTestHandler (isMain) {
216
205
  _ddIsModified: isModified
217
206
  } = test
218
207
 
219
- const testName = removeEfdStringFromTestName(removeAttemptToFixStringFromTestName(test.fullTitle()))
220
-
221
208
  const testInfo = {
222
- testName,
209
+ testName: test.fullTitle(),
223
210
  testSuiteAbsolutePath,
224
211
  title,
225
212
  testStartLine
@@ -445,7 +432,6 @@ function getRunTestsWrapper (runTests, config) {
445
432
  retryTest(
446
433
  test,
447
434
  config.testManagementAttemptToFixRetries,
448
- addAttemptToFixStringToTestName,
449
435
  ['_ddIsAttemptToFix', isDisabled && '_ddIsDisabled', isQuarantined && '_ddIsQuarantined']
450
436
  )
451
437
  } else if (isDisabled) {
@@ -469,7 +455,6 @@ function getRunTestsWrapper (runTests, config) {
469
455
  retryTest(
470
456
  test,
471
457
  config.earlyFlakeDetectionNumRetries,
472
- addEfdStringToTestName,
473
458
  ['_ddIsModified', '_ddIsEfdRetry']
474
459
  )
475
460
  }
@@ -488,7 +473,6 @@ function getRunTestsWrapper (runTests, config) {
488
473
  retryTest(
489
474
  test,
490
475
  config.earlyFlakeDetectionNumRetries,
491
- addEfdStringToTestName,
492
476
  ['_ddIsNew', '_ddIsEfdRetry']
493
477
  )
494
478
  }
@@ -772,7 +772,10 @@ addHook({
772
772
  if (!isKnownTestsEnabled && !isTestManagementTestsEnabled && !isImpactedTestsEnabled) {
773
773
  return oldCreateRootSuite.apply(this, arguments)
774
774
  }
775
- const rootSuite = await oldCreateRootSuite.apply(this, arguments)
775
+
776
+ const createRootSuiteReturnValue = await oldCreateRootSuite.apply(this, arguments)
777
+ // From v1.56.0 on, createRootSuite returns `{ rootSuite, topLevelProjects }`
778
+ const rootSuite = createRootSuiteReturnValue.rootSuite || createRootSuiteReturnValue
776
779
 
777
780
  const allTests = rootSuite.allTests()
778
781
 
@@ -861,7 +864,7 @@ addHook({
861
864
  }
862
865
  }
863
866
 
864
- return rootSuite
867
+ return createRootSuiteReturnValue
865
868
  }
866
869
 
867
870
  // We need to proxy the createRootSuite function because the function is not configurable
@@ -0,0 +1,17 @@
1
+ 'use strict'
2
+
3
+ const CompositePlugin = require('../../dd-trace/src/plugins/composite')
4
+ const AnthropicTracingPlugin = require('./tracing')
5
+ const AnthropicLLMObsPlugin = require('../../dd-trace/src/llmobs/plugins/anthropic')
6
+
7
+ class AnthropicPlugin extends CompositePlugin {
8
+ static id = 'anthropic'
9
+ static get plugins () {
10
+ return {
11
+ llmobs: AnthropicLLMObsPlugin,
12
+ tracing: AnthropicTracingPlugin
13
+ }
14
+ }
15
+ }
16
+
17
+ module.exports = AnthropicPlugin
@@ -0,0 +1,30 @@
1
+ 'use strict'
2
+
3
+ const TracingPlugin = require('../../dd-trace/src/plugins/tracing')
4
+
5
+ class AnthropicTracingPlugin extends TracingPlugin {
6
+ static id = 'anthropic'
7
+ static operation = 'request'
8
+ static system = 'anthropic'
9
+ static prefix = 'tracing:apm:anthropic:request'
10
+
11
+ bindStart (ctx) {
12
+ const { resource, options } = ctx
13
+
14
+ this.startSpan('anthropic.request', {
15
+ meta: {
16
+ 'resource.name': `Messages.${resource}`,
17
+ 'anthropic.request.model': options.model
18
+ }
19
+ }, ctx)
20
+
21
+ return ctx.currentStore
22
+ }
23
+
24
+ asyncEnd (ctx) {
25
+ const span = ctx.currentStore?.span
26
+ span?.finish()
27
+ }
28
+ }
29
+
30
+ module.exports = AnthropicTracingPlugin